Skip to content

fix(docker): keep the venv on PATH in login shells - #72197

Closed
ugoenyioha wants to merge 1 commit into
NousResearch:mainfrom
ugoenyioha:fix/docker-venv-path-login-shells
Closed

fix(docker): keep the venv on PATH in login shells#72197
ugoenyioha wants to merge 1 commit into
NousResearch:mainfrom
ugoenyioha:fix/docker-venv-path-login-shells

Conversation

@ugoenyioha

Copy link
Copy Markdown

Summary

Split out of #33179 as the focused docker-lane change @benbarclay asked for. One RUN line; the other two concerns from that PR are addressed separately (see the bottom).

ENV PATH puts /opt/hermes/bin and the venv ahead of the system paths, and ordinary processes inherit it. A login shell does not — it rebuilds PATH from /etc/profile and drops all of it.

Answering the question from #33179

Can you describe the path that hits this? … What's hitting bash -l inside the container? (Terminal-tool sessions? docker exec -it … bash -l? Something else?)

Terminal-tool sessions. tools/environments/docker.py::_run_bash() — docstring "Spawn a bash process inside the Docker container" — does:

cmd = [self._docker_exe, "exec"]
...
# Only inject -e env args during init_session (login=True).
if login:
    cmd.extend(self._init_env_args)
cmd.extend([self._container_id])
if login:
    cmd.extend(["bash", "-l", "-c", cmd_string])
else:
    cmd.extend(["bash", "-c", cmd_string])

init_session is the caller that passes login=True, so environment snapshotting runs under bash -l inside the container. Your read of main-wrapper.sh and the s6 run scripts was right — none of those are login shells. This is a different entry point.

Measured, not assumed

In a container built from this image, with no env overrides:

$ bash -c  'command -v python3; echo $PATH'
/opt/hermes/.venv/bin/python3
/opt/hermes/bin:/opt/hermes/.venv/bin:/opt/data/.local/bin:/usr/local/sbin:...

$ bash -lc 'command -v python3; echo $PATH'
/usr/bin/python3
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

The login shell loses the venv entirely and python3 falls back to the system interpreter, which has none of Hermes' dependencies. The failures that follow read as missing packages rather than as a PATH problem, which is what makes this slow to diagnose rather than merely broken.

One deviation from #33179

That PR's line prepended only /opt/hermes/.venv/bin:/opt/data/.local/bin, omitting /opt/hermes/bin. The ENV PATH comment right above explains why that comes first — it is the privilege-drop shim, and it must win resolution. Omitting it would leave login shells with the opposite precedence to every other process. This version mirrors the ENV ordering exactly.

Scope

`ENV PATH` puts /opt/hermes/bin and the venv ahead of the system paths, and
ordinary processes inherit it. A LOGIN shell does not: it rebuilds PATH from
/etc/profile and drops all of it.

That path is exercised in normal operation. The terminal tool starts its
in-container sessions with a login shell —
tools/environments/docker.py::_run_bash() runs `docker exec … bash -l -c`
whenever login=True, which is how init_session snapshots the environment.

Measured in a container built from this image:

    bash -c   → python3 -> /opt/hermes/.venv/bin/python3
    bash -lc  → python3 -> /usr/bin/python3

so those sessions get the system interpreter, which has none of Hermes'
dependencies. The resulting failures read as missing packages rather than as a
PATH problem, which makes them slow to diagnose.

Drop a /etc/profile.d/hermes-venv.sh that re-exports the same prefix. The
ordering deliberately mirrors the ENV above with /opt/hermes/bin first: that is
the privilege-drop shim, and it must keep winning PATH resolution in login
shells too. Prepending only the venv would silently invert that precedence.

Split out of NousResearch#33179 as a focused docker-lane change, per review. The
S6_KEEP_ENV hunk from that PR is dropped -- it is a no-op now that NousResearch#32412
landed (verified separately on NousResearch#33148) -- and the Mattermost reply-mode feature
it also carried is NousResearch#33149.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists area/docker Docker image, Compose, packaging backend/docker Docker container execution tool/terminal Terminal execution and process management labels Jul 26, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to #64849 and #56642: all address the Debian login-shell PATH reset. This PR is the focused image-layer variant; #64849 adds broader Docker environment-forwarding coverage, while #56642 repairs the local shell runner.

@ugoenyioha

Copy link
Copy Markdown
Author

Closing as a duplicate — #64849 predates this and is strictly more complete: a versioned docker/profile.d/99-hermes-venv-path.sh with an idempotency guard, plus the snapshot layer in tools/environments/{base,docker,local}.py that this PR ignores, plus tests. #56642 covers the terminal-snapshot half of the same root cause.

I should have run the duplicate search in CONTRIBUTING's "Before You Start" section before writing this. I've moved the useful part — a before/after PATH measurement from a pristine container, and the caller that puts this on the normal terminal-tool path (tools/environments/docker.py::_run_bash() with login=True, via init_session) — to #64849 as review evidence.

No action needed here; #64849 is the one to land.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docker Docker image, Compose, packaging backend/docker Docker container execution P2 Medium — degraded but workaround exists tool/terminal Terminal execution and process management type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants