Summary
The prompt/plugin preset setup.sh builds a per-run virtualenv with uv venv .venv then installs the SDK with uv pip install …. If the environment already has VIRTUAL_ENV set (which happens whenever the local agent-server is itself launched via uv run), uv pip install targets that inherited active venv instead of the freshly-created .venv. The run's .venv stays empty, setup.sh still prints [setup] Done, and main.py then dies with:
ModuleNotFoundError: No module named 'openhands'
This makes local-agent-server mode unusable on any deployment where the agent-server process runs under uv run (its child bash shells inherit VIRTUAL_ENV, and automation runs are dispatched as bash commands on that agent-server).
Where
openhands/automation/presets/prompt/setup.sh (lines ~40, ~43)
openhands/automation/presets/plugin/setup.sh (lines ~40, ~43)
uv venv .venv --python '>=3.12' --quiet
uv pip install --quiet "openhands-sdk==${SDK_VERSION}" ...
Root cause
uv pip install installs into $VIRTUAL_ENV when that variable is set, in preference to the .venv in the working directory. uv venv does not clear it.
Repro (local mode)
- Run the automation service in local mode against an agent-server that was started with
uv run python -m openhands.agent_server ….
- Create a prompt automation and dispatch it.
setup.sh reports success; the run fails at import openhands.
- Inspect the run's
.venv/lib/python*/site-packages/ — it only contains _virtualenv.pth (empty); the SDK landed in the agent-server's venv instead.
Confirmed directly: a bash command run through the agent-server prints VIRTUAL_ENV=/…/agent-sdk/.venv; after adding unset VIRTUAL_ENV before the uv calls, the same install populates the run's .venv correctly.
Suggested fix
Make setup.sh robust to an inherited VIRTUAL_ENV. Either:
- Target the venv explicitly (preferred — no reliance on activation state):
uv venv .venv --python '>=3.12' --quiet
uv pip install --python .venv/bin/python --quiet "openhands-sdk==${SDK_VERSION}" ...
(Windows preset path uses .venv/Scripts/python.exe.)
- Or
unset VIRTUAL_ENV at the top of setup.sh before the uv calls.
Targeting --python .venv/bin/python is the most defensive since it works regardless of any activation state in the parent process.
Notes
Found while wiring the Automation service in local mode against an upstream Python agent-server (dogfooding self-hosted). Happy to open a PR with the --python .venv/bin/python change to both preset setup.sh files if that's welcome.
Summary
The prompt/plugin preset
setup.shbuilds a per-run virtualenv withuv venv .venvthen installs the SDK withuv pip install …. If the environment already hasVIRTUAL_ENVset (which happens whenever the local agent-server is itself launched viauv run),uv pip installtargets that inherited active venv instead of the freshly-created.venv. The run's.venvstays empty,setup.shstill prints[setup] Done, andmain.pythen dies with:This makes local-agent-server mode unusable on any deployment where the agent-server process runs under
uv run(its child bash shells inheritVIRTUAL_ENV, and automation runs are dispatched as bash commands on that agent-server).Where
openhands/automation/presets/prompt/setup.sh(lines ~40, ~43)openhands/automation/presets/plugin/setup.sh(lines ~40, ~43)Root cause
uv pip installinstalls into$VIRTUAL_ENVwhen that variable is set, in preference to the.venvin the working directory.uv venvdoes not clear it.Repro (local mode)
uv run python -m openhands.agent_server ….setup.shreports success; the run fails atimport openhands..venv/lib/python*/site-packages/— it only contains_virtualenv.pth(empty); the SDK landed in the agent-server's venv instead.Confirmed directly: a bash command run through the agent-server prints
VIRTUAL_ENV=/…/agent-sdk/.venv; after addingunset VIRTUAL_ENVbefore theuvcalls, the same install populates the run's.venvcorrectly.Suggested fix
Make
setup.shrobust to an inheritedVIRTUAL_ENV. Either:.venv/Scripts/python.exe.)unset VIRTUAL_ENVat the top ofsetup.shbefore theuvcalls.Targeting
--python .venv/bin/pythonis the most defensive since it works regardless of any activation state in the parent process.Notes
Found while wiring the Automation service in local mode against an upstream Python agent-server (dogfooding self-hosted). Happy to open a PR with the
--python .venv/bin/pythonchange to both presetsetup.shfiles if that's welcome.