Skip to content

fix(agent-server): keep tmux socket paths within the Unix socket limit - #5559

Merged
enyst merged 1 commit into
mainfrom
claude/sharp-wright-o4pmhb
Oct 8, 2026
Merged

enyst merged 1 commit into
mainfrom
claude/sharp-wright-o4pmhb

Conversation

@enyst

@enyst enyst commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

HUMAN:

I, Engel Nyst (@enyst), asked an AI agent to help with project maintenance work.


AGENT:

I'm an AI agent based on Opus 5.5, helping Engel Nyst (@enyst) with project maintenance.

Why

Since #5485, each terminal pool gets its own tmux socket, openhands-<32 hex>. That makes the socket path 33 bytes longer, so a TMUX_TMPDIR deeper than about 54 bytes (Linux) or 51 bytes (macOS) no longer fits sun_path. Every terminal then fails with LibTmuxException ... (File name too long), and the conversation's first message returns 500. Agent Canvas's live E2E job has failed this way on every run since it pinned 1.53.0 (OpenHands/OpenHands#18082). The server's own default under macOS's $TMPDIR is over the limit too.

Summary

  • The agent-server checks whether $TMUX_TMPDIR/tmux-<uid>/openhands-<32 hex> fits the platform's socket path limit. If the configured or default directory is too deep, it uses /tmp/openhands-agent-server-<pid> instead.
  • It logs a warning when it overrides an explicit TMUX_TMPDIR, and restores the original value on shutdown.
  • A short, explicit TMUX_TMPDIR is honored exactly as before. Socket naming and the isolation from fix(terminal): isolate tmux sockets to prevent cross-conversation handle reuse #5485 are unchanged.

Issue Number

Fixes #5558

How to Test

Unit tests:

uv run pytest tests/agent_server/test_api.py tests/tools/terminal/test_tmux_pane_pool.py tests/tools/terminal/test_conversation_cleanup.py -n 4
81 passed
uv run pre-commit run --files openhands-agent-server/openhands/agent_server/api.py tests/agent_server/test_api.py
all hooks passed

The new test_ensure_server_tmux_tmpdir_replaces_too_deep_env starts a real TmuxPanePool under a too-deep TMUX_TMPDIR. The existing tmux-dir tests now use a short temp dir, because pytest's tmp_path is itself too deep for a socket, which is the same trap #5494 hit.

Before and after, with a 64-byte-deep TMUX_TMPDIR:

--- main
TMUX_TMPDIR in use: /tmp/tmpth5i3was/dddd…dddd | replaced: False
terminal pool FAILED: LibTmuxException ['error connecting to /tmp/tmpth5i3was/dddd…/tmux-0/openhands-56264a9c…db (File name too long)
--- this branch
TMUX_TMPDIR in use: /tmp/openhands-agent-server-<pid> | replaced: True
terminal pool initialized OK

End to end through Agent Canvas, the real downstream workflow:

  • I ran OpenHands' live E2E conversation spec from a checkout at the CI path, /home/runner/work/OpenHands/OpenHands.
  • Its TMUX_TMPDIR was the stock .tmp/live-e2e-state/tmux, and OH_AGENT_SERVER_LOCAL_PATH pointed at this branch.
  • A local stand-in LLM returned the spec's terminal call and reply.

On 1.53.0 that spec fails with File name too long. On this branch the server logs the following, and the spec passes:

WARNING openhands.agent_server.api: TMUX_TMPDIR /home/runner/work/OpenHands/OpenHands/.tmp/live-e2e-state/tmux is too long for tmux socket paths; using per-server tmux directory /tmp/openhands-agent-server-31806 instead
  1 passed (1.1m)

I did not run this on macOS. The macOS numbers are computed from the 104-byte sun_path.

Video/Screenshots

N/A (server-side change; logs above).

Design Doc

N/A

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

  • fix(e2e): let the live Agent Server E2E job run and finish OpenHands#18084 gives Agent Canvas's live E2E job a short TMUX_TMPDIR so it can go green before a release with this fix. This PR is the general fix for every deployment.
  • Shortening the socket suffix instead would not be enough: /var/folders/…/T/openhands-agent-server-<pid> on macOS is too deep even for a 12-hex suffix.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DdnhYnKDzEpfWiZNr8kDsa


Generated by Claude Code


🐳 Agent Server images for this PR — GHCR package, pull/run commands, and all pushed tags (click to expand)

• GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python-slim amd64, arm64 python-node-runtime Link
python-minimal amd64, arm64 python-node-runtime Link
python amd64, arm64 python-node-runtime Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:55f5488-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-55f5488-python \
  ghcr.io/openhands/agent-server:55f5488-python

All tags pushed for this build

ghcr.io/openhands/agent-server:55f5488-golang-amd64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-golang-amd64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-golang-amd64
ghcr.io/openhands/agent-server:55f5488-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:55f5488-golang-arm64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-golang-arm64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-golang-arm64
ghcr.io/openhands/agent-server:55f5488-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:55f5488-java-amd64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-java-amd64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-java-amd64
ghcr.io/openhands/agent-server:55f5488-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:55f5488-java-arm64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-java-arm64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-java-arm64
ghcr.io/openhands/agent-server:55f5488-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:55f5488-python-amd64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-amd64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-amd64
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-amd64
ghcr.io/openhands/agent-server:55f5488-python-arm64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-arm64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-arm64
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-arm64
ghcr.io/openhands/agent-server:55f5488-python-minimal-amd64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-minimal-amd64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-minimal-amd64
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-minimal-amd64
ghcr.io/openhands/agent-server:55f5488-python-minimal-arm64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-minimal-arm64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-minimal-arm64
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-minimal-arm64
ghcr.io/openhands/agent-server:55f5488-python-slim-amd64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-slim-amd64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-slim-amd64
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-slim-amd64
ghcr.io/openhands/agent-server:55f5488-python-slim-arm64
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-slim-arm64
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-slim-arm64
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-slim-arm64
ghcr.io/openhands/agent-server:55f5488-golang
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-golang
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-golang
ghcr.io/openhands/agent-server:55f5488-golang_tag_1.21-bookworm
ghcr.io/openhands/agent-server:55f5488-java
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-java
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-java
ghcr.io/openhands/agent-server:55f5488-eclipse-temurin_tag_17-jdk
ghcr.io/openhands/agent-server:55f5488-python-minimal
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-minimal
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-minimal
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-minimal
ghcr.io/openhands/agent-server:55f5488-python-slim
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python-slim
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python-slim
ghcr.io/openhands/agent-server:55f5488-python-node-runtime-slim
ghcr.io/openhands/agent-server:55f5488-python
ghcr.io/openhands/agent-server:55f54885276b425b1d59b3a9de195ab7be77e4a6-python
ghcr.io/openhands/agent-server:claude-sharp-wright-o4pmhb-python
ghcr.io/openhands/agent-server:55f5488-python-node-runtime

About Multi-Architecture Support

  • Each variant tag (e.g., 55f5488-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 55f5488-python-amd64) are also available if needed

Since terminals got unique socket names (openhands-<32 hex>, #5485), any
TMUX_TMPDIR longer than about 54 bytes on Linux (about 51 on macOS) makes
every terminal fail with "LibTmuxException: new-session: error connecting
to ... (File name too long)": <dir>/tmux-<uid>/<name> no longer fits
sun_path (108 bytes on Linux, 104 on macOS, NUL included). Agent Canvas
hits this in CI, where TMUX_TMPDIR sits under the checkout's .tmp state
dir, and the server's own default under macOS's per-user $TMPDIR is too
deep as well.

The agent-server now falls back to a per-server directory under /tmp when
the configured or default TMUX_TMPDIR cannot hold a socket path, warns
when it overrides an explicit value, and restores that value on shutdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdnhYnKDzEpfWiZNr8kDsa
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

REST API breakage checks (OpenAPI) — ✅ PASSED

Result: ✅ PASSED

Action log

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-agent-server/openhands/agent_server
   api.py3522393%127, 168, 182–185, 253, 259, 315, 320, 333–335, 356, 374, 380, 384, 405–406, 599, 665, 668, 674
TOTAL481111921360% 

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved ✅

Reviewed the diff (openhands-agent-server/.../api.py, tests/agent_server/test_api.py) at head 55f5488, linked issue #5558 and its acceptance criteria, and current-head CI (all green, including agent-server-tests, workspace-tests, windows-tests).

What I checked and confirmed:

  • Socket-path math is right. sockaddr_un.sun_path is 108 bytes on Linux and 104 on macOS including the NUL, so the 107/103 maxima are correct. I reproduced the boundary against real tmux on Linux: a 107-byte $TMUX_TMPDIR/tmux-<uid>/openhands-<32hex> path starts a server; 108 fails with File name too long.
  • os.path.realpath() in _fits_tmux_socket is load-bearing, not incidental. tmux resolves $TMUX_TMPDIR before binding — I verified a short symlink pointing at a deep target fails, while a deep symlink pointing at a short target works. Using the resolved path both rejects the first case (correctly) and accepts the second (correctly); without it, the first would be accepted and break. Nice detail.
  • The /tmp fallback fits. On macOS /tmp resolves to /private/tmp, giving ~87 bytes for the socket path, under 103; the stock macOS $TMPDIR (~131 bytes) is correctly detected as too deep.
  • The restore change fixes a real bug. Reinstating original_tmux_tmpdir instead of unconditionally popping means an explicit-but-too-deep value is no longer left unset after shutdown.
  • Acceptance criteria covered. Deep explicit TMUX_TMPDIR → overridden with a warning and a working pool (the new test starts a real TmuxPanePool); unset + deep temp dir → fallback fits; short explicit value → honored unchanged. The per-pool socket naming and #5485 isolation are untouched.
  • Evidence. The PR body shows the original symptom on the base revision, the same workflow passing on this head, plus the real OpenHands live E2E spec through Agent Canvas with logs, and explicitly flags that macOS was computed rather than reproduced. That matches what I'd expect for a runtime-lifecycle bug fix.

Non-blocking observations (no action needed for approval):

  • The /tmp fallback is not re-validated with _fits_tmux_socket; it happens to fit on both platforms today, so it's fine, but it is a hardcoded assumption.
  • Repo memory in AGENTS.md still states the server "respects an explicit TMUX_TMPDIR override" unconditionally; a one-line update noting the depth guard would keep it accurate.

This review was created by an AI agent (OpenHands) on behalf of the requesting user.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Terminals fail with "File name too long" when TMUX_TMPDIR is deep, since per-pool tmux socket names

3 participants