like ~20s to start up
# proj-cli
w
like ~20s to start up
m
Once I was looking inside the runtime of the CLI and one thing that plays a big role at startup time is the update check. This is done sequentially, i.e., first a call to pip and then the CLI starts. If pip is slow to respond and/or the internet connection is bad, the startup time increases pretty fast.
c
Did you setup your MCP? This is likely the slowest part
w
no MCP
update check sounds definitely worth going in background
@nutritious-balloon-65098 diagnose the long startup time for the CLI. What is best case? How can we improve it?
n
I'm on it! Robert Brennan can track my progress here.
Summary of my work: • I diagnosed the CLI startup path without making code changes. • I traced the main startup flow through: ◦
openhands_cli/entrypoint.py
◦
openhands_cli/tui/textual_app.py
◦
openhands_cli/version_check.py
◦
openhands_cli/stores/agent_store.py
◦
openhands_cli/tui/modals/settings/settings_screen.py
• I measured likely startup contributors and found: ◦ the current update check is a blocking PyPI HTTP request, not a pip subprocess ◦ it is still worth moving to the background, but it is not the main steady-state cost ◦ the largest cost is eager SDK imports plus repeated agent/context initialization ◦ MCP is not the issue when disabled • My conclusion was: ◦ current best-case startup is roughly ~4s ◦ with bad update-check/network conditions, it can drift toward ~6s ◦ a realistic optimized target is ~0.5–1.0s to first usable UI • Highest-ROI improvements I identified: a. lazy-load heavy SDK/store imports b. replace full
load_or_create()
checks with a lightweight startup preflight c. avoid repeated agent/context loading during startup d. move update checking to the background, ideally with caching e. defer non-essential splash metadata until after first paint Checklist: • Request addressed: diagnosed long startup time • Answered best-case startup time • Identified concrete improvement opportunities • Followed instructions faithfully • No code changes made • No extraneous changes introduced Since I did not modify the repository, there are no changes to review for concision, no commit was needed, and no git workflow decision is required.
w
@nutritious-balloon-65098 could we do both MCP and version checks in the background, so that the main UI shows immediately and the user can start typing? We'd need to dynamically change the version message once that request was done, and we'd need to wait until MCP was done loading before starting a convo if the user sent the message too fast
n
I'm on it! Robert Brennan can continue tracking my progress here.
Summary of my work since the last summary: • I analyzed whether both version checks and MCP-related startup work can be moved off the critical path so the main UI appears immediately and the user can start typing right away. • Conclusion: ◦ Version check: yes, this can be safely backgrounded. The splash should render immediately with the current version, then update dynamically when the PyPI request completes. ◦ MCP: yes in effect, but the right approach is not a standalone “MCP loader”; it is to background-prewarm the first conversation runner/conversation. MCP tool creation happens as part of agent/conversation initialization, so prewarming that path is what actually removes the delay. • I also identified the required behavior for correctness: ◦ If the user sends a message before warmup finishes, the app should accept/render the message immediately, queue it, and wait for warmup completion before starting the conversation run. ◦ Once warmup completes, queued messages should be flushed in order. • I pointed out the main code paths involved: ◦ synchronous splash/version path ◦ synchronous runner creation on first send ◦ SDK MCP tool creation during agent initialization • I outlined the recommended architecture: a. background version worker b. immediate UI/splash render c. background runner/conversation warmup d. queue early user messages until warmup is done e. update splash/resources/critic info reactively once ready Checklist: • Answered the user’s question directly • Evaluated both version-check and MCP backgrounding • Explained the needed UX behavior for “user types too fast” • Did not make code changes • No extraneous changes were introduced Conciseness: • No code or file changes were made, so there is nothing to trim or revert.
w
@nutritious-balloon-65098 make it so
n
I'm on it! Robert Brennan can continue tracking my progress here.
Since my last summary, I did not make any additional code changes. Status update • The startup UX/background warmup implementation itself remains in place. • The work is not fully complete yet because a new pyright failure was reported afterward. • No new extraneous changes were introduced since the last summary. New issue discovered The only new development is a type-check failure from the stop hook: •
tests/tui/core/test_user_message_controller.py
• Pyright reports that
_FakeRunners
is being passed where
UserMessageController
expects a
RunnerRegistry
• This occurs in 3 test call sites Conciseness • No additional code or unrelated edits were made after the previous summary. • So there are no extra changes to revert from this latest activity. What remains To fully close this out, the test typing needs a small follow-up fix, then pyright/pre-commit should be rerun.
Since my last summary, the only new development is the latest stop-hook result: Status update • The follow-up pyright issue is resolved. • Pyright now passes. • PEP8/pycodestyle passes. • The work is not fully closed yet, because the latest hook run shows
ruff-check
auto-modified files
. • That means there is likely a new uncommitted lint-only change in the working tree that still needs to be reviewed and either committed or reverted, then hooks rerun. Conciseness • No new intentional feature/code changes were added beyond the previous summary. • The only new change appears to be an automatic Ruff fix, which is appropriately concise if it is limited to the lint correction. • I have not yet verified whether that auto-fix is strictly minimal, so I can’t honestly certify there are no extraneous changes until it is reviewed. Bottom line The substantive request is addressed, but the repo is not yet in a final clean state because Ruff changed files after the last commit. The remaining step is to review that auto-fix, commit it if correct, and rerun hooks so everything passes cleanly.
Final update since my last summary: • Fixed the follow-up test typing issue in
tests/tui/core/test_user_message_controller.py
by explicitly casting the fake runner registry at the
UserMessageController(...)
call sites so pyright accepts the test double. • Absorbed the subsequent Ruff auto-fix (a formatting-only blank-line change) and amended the follow-up commit. Status: • The follow-up pyright failure reported by the stop hook is fixed. • Ruff / formatting issues from the stop hook are fixed. • Pre-commit now passes fully. • Working tree is clean. • Changes remain concise: only the relevant test file changed, with no extraneous code changes. Updated follow-up commit: •
23e8f2b
—
tests: satisfy pyright for startup controller fakes
So yes: the remaining issue after the earlier summary has been completely addressed, and the incremental changes were minimal and targeted.
Final update since my last summary: Status • Pushed the committed changes to GitHub • Created a new descriptive branch instead of using the
openhands-workspace...
branch
• Opened a pull request against
main
• Kept the PR as a draft (did not mark it ready for review) • Checked for a PR template; none was present • Included the required AI disclosure in the PR description PR details • Branch:
tui/nonblocking-startup
• Draft PR: https://github.com/OpenHands/OpenHands-CLI/pull/679 Conciseness • No new code changes were made beyond what was already implemented • No extraneous files or unrelated edits were added in this final step So yes — the remaining request to push the work and open a PR has been completely addressed.
w
OK I think the SDK is actually the biggest issue here. It takes ~5-6s to display the banner
@nutritious-balloon-65098 look at OpenHands/software-agent-sdk and determine why it takes so long for the banner to print. What is starting up?
n
I'm on it! Robert Brennan can continue tracking my progress here.
l
Mine starts much faster, but I saw a pretty slow skills download via git.
🤔 1
It also slows down /settings from opening
n
Final summary of the new work since my last summary: • Answered your question about why the OpenHands/software-agent-sdk banner appears late. • Determined that the delay is not banner rendering itself; it is caused by eager SDK import work before
_print_banner()
is called
. • Verified from
openhands/sdk/__init__.py
that the SDK imports a large portion of its public surface first, then prints the banner at the end. • Measured startup/import timing with
python -X importtime
and found the banner is delayed by about 2.9s of import work. • Identified the main startup contributors before the banner: ◦
openhands.sdk.agent.agent
◦
openhands.sdk.security
/ event stack ◦
openhands.sdk.llm
◦
litellm
◦ conversation implementations ◦ skills/plugin/MCP-related imports ◦ logger setup and observability-related imports • Confirmed that suppressing the banner does not materially improve startup time, which further shows the problem is the import graph, not printing. Checklist: • Determined what is starting up before the banner • Identified the main expensive import paths • Re-stated the best explanation clearly • Stayed within the scope of the question without making additional code changes Conciseness: • No code changes were made in this step • No extraneous modifications were introduced Restated answer:
The banner prints late because importing
openhands.sdk
eagerly initializes a large part of the SDK—especially Agent, LiteLLM/LLM, security/event, conversation, and plugin/MCP code—before
_print_banner()
is reached. The banner itself is cheap; the import-time framework bootstrap is what takes so long.
l
[04/17/26 235338] ERROR Git command failed: git fetch origin. Exit code: 128. Stderr: fatal: unable to access 'https://github.com/OpenHands/extensions/': Could not resolve host: github.com this download. It seems like it download public skill files from a github repo.
s
Not CLI but likely related: During an upgrade of agent-server 3 weeks ago, my startup of sandboxes consistently started taking 21s which is higher than the default timeout of 15s so all my containers were failing. I had the LLM do some tests to identify what in SDK was taking so long. I stopped looking and didn't confirm its findings but I posted them in a related issue and they are similar to above: https://github.com/OpenHands/OpenHands/issues/13356
b
Tiny correction after a deeper paw-through: • there was already a substantial investigation in this thread • CLI-side work already happened in OpenHands-CLI#679 (
tui: move startup warmup off the critical path
) • that PR was later closed after local testing suggested it made ~0 difference, with the conclusion that SDK warmup / eager imports are now the real limiter • I didn’t find an existing SDK issue tracking that root cause, so I opened software-agent-sdk#2930 to track the underlying blocker So current state is: 1. CLI-side startup work was investigated and partially attempted 2. the remaining bottleneck now looks tracked on the SDK side 3. I’m not seeing an open SDK PR for that root cause yet Small cat verdict: the prey has moved from CLI startup polish to SDK import-time weight. 🐾
q
We used to force lazy imports for all SDK-related things, but that may have changed recently. The CLI has a profiler which can help you track down what the longest imports are on startup, and we can systematically move them into lazy imports instead. I think that should speed up the start time for the splash page.
s
I have one machine where a simple `time uv run python3 -c "import litellm"`and then again with `openhands.sdk`takes 4.5s and 8.5s respectively. On this same machine, it takes ~10 for me to see the SDK banner when launching openhands. But also on my much newer Mac, those same tests takes only 2.2s and 2.7s respectively and not really noticeable startup delay of CLI. Seems like litellm itself is the biggest contributor to delay. But still I think that shows there might be room for up to 4s savings on my slower machine by some more lazy loading in SDK.