OpenHands / OpenHands/software-agent-sdk
[Bug]: Deferred initialization failure leaves a partially initialized runtime
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 539
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 137
Description
Bug Description
InitService returns to dormant after an initialization exception without unwinding resources or restoring the process/app state already mutated by that attempt.
Initialization progressively changes environment/configuration, telemetry, the module-level conversation-service singleton, entered conversation/bash services, app.state, and app.root_path in openhands-agent-server/openhands/agent_server/init_router.py:223-268. The exception path at :271-278 resets only _error and _state.
A retry can therefore inherit live services, credentials, telemetry, or configuration from the failed attempt. A later retry can also overwrite the only cleanup reference for a resource entered by the failed attempt.
Expected Behavior
Initialization should be transactional: dormant must imply that no staged runtime resources or configuration from a failed attempt remain live, and a retry should start cleanly.
Actual Behavior
An independent, self-contained reproducer is posted in the issue comments. Save
it as /tmp/repro_4658.py, then run:
uv run python /tmp/repro_4658.py
On SDK commit 6d3881035, it exercises real FastAPI, InitService, ConversationService, BashEventService, filesystem failure, and teardown; no service mocks and prints:
http_status=500
state_after_failure=dormant
env_after_failure=leaked-from-failed-init
conversation_singleton_replaced=True
bash_service_still_retained=True
telemetry_sink_published=True
Suggested Implementation
Build a private runtime locally using an AsyncExitStack (effective config, conversation service, bash service, telemetry and other resources). Publish it to app.state and swap any compatibility singleton only at one successful commit point. On failure, unwind the stack and restore staged environment/global values before returning to dormant.
The ordinary and deferred startup paths should ultimately resolve the same app-owned runtime, while preserving the current REST contract.
Acceptance Criteria
- Failure injected after each resource-entry phase exits every entered resource exactly once.
- Failed initialization leaves bootstrap authentication,
app.state, root path, environment, telemetry, and service globals unchanged. - A retry after every injected failure succeeds.
- Successful teardown preserves the existing service exit order.
-
InitRequest,InitStatus, HTTP statuses, headers, and OpenAPI remain unchanged.
Version
Current main at 6d3881035. Found during a read-only state/ownership audit; no repository files were changed and no tests were run.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with openhands-agent-server/openhands/agent_server/init_router.py:223-278 and run the comment-provided /tmp/repro_4658.py reproducer with uv. Trace each initialization resource and the existing teardown order, then add failure-injection coverage for the listed phases. Done means failed attempts leave state unchanged, retries succeed, teardown order remains intact, and the REST contract is unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- fastapi, python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100