Skip to content

Controlled Codex launch fails when $CODEX_HOME/app-server-control is a symlink #275

Description

@schickling

Problem

st2 driver codex's controlled launch always passes --listen (run_controlled_owned → the --listen boundary at src/codex_app_server.rs:1440). On that path the Codex app-server refuses to start when $CODEX_HOME/app-server-control is a symlink, even when the symlink target is a real, existing directory:

Error: socket directory path exists and is not a directory: <CODEX_HOME>/app-server-control

The wrapper surfaces this as a failed launch; the error text lands in app-server.log.

Fleet evidence

~/.codex/app-server-control is a symlink on at least one machine in this fleet — it points at ~/.codex-schickling-j/app-server-control, part of a per-account ~/.codex layout in which nearly every entry is symlinked. On that host, controlled Codex launch cannot start at all.

Reproduction

T=$(mktemp -d "$HOME/probe.XXXX"); mkdir -p "$T/real-control" "$T/home" "$T/sock"
printf 'model = "gpt-5.6-sol"\n' > "$T/home/config.toml"

# A — symlinked control dir
ln -s "$T/real-control" "$T/home/app-server-control"
CODEX_HOME="$T/home" timeout 10 codex app-server --listen "unix://$T/sock/a.sock" </dev/null
# -> rc=1, "socket directory path exists and is not a directory: .../app-server-control"

# B — real control dir (control case)
rm "$T/home/app-server-control"; mkdir -p "$T/home/app-server-control"
CODEX_HOME="$T/home" timeout 10 codex app-server --listen "unix://$T/sock/b.sock" </dev/null
# -> rc=124 (still running = started successfully)

Scope, established rather than assumed

  • Requires --listen. Bare codex app-server starts cleanly with the symlink in place, in both configurations. This is why an initial probe failed to reproduce it.
  • Not the socket path. The --listen target above is inside a real directory and it still refuses.
  • Not CODEX_HOME itself. In the reproduction CODEX_HOME is a real directory; only its app-server-control child is a symlink, and that alone is sufficient.
  • Not a regression, and not specific to an unadmitted version. Reproduced identically on codex-cli 0.146.0 — currently admitted by SUPPORTED_CODEX_CLI_VERSIONS — and on 0.147.0. Untested on 0.145.0. This is therefore a live failure against a supported version today, and is independent of st2 refuses the locally installed codex-cli, and the version pin is now behind by one release #267 / Admit codex-cli 0.147.0 after repeating the pin's policy checks #272.
  • The symlink target exists and is a real directory in every failing case, so this looks like an lstat-vs-stat check upstream rather than a broken link.

Options

Upstream-facing: the check appears over-strict — a symlink resolving to a directory is a directory for every purpose the socket path needs.

st2-facing, if a workaround is wanted before upstream changes: the controlled launch could resolve $CODEX_HOME/app-server-control before handing it over, or doctor could detect the symlink and report it as a known-fault precondition rather than letting the launch fail with a message that appears only in app-server.log.

No preference asserted here — the surfacing question in particular is a maintainer call.

Acceptance

  • The failure mode is either fixed upstream, worked around in the controlled launch, or reported as a precondition by doctor rather than only in app-server.log.
  • Behavior on 0.145.0 confirmed.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:driverHarness drivers: launch, MCP, app-server, native delivery · Set: manualharness:codexCodex-specific behavior · Set: manualorigin:agentFiled or primarily produced by an AI agent · Set: manualtype:bugSomething broken or a regression · Set: manual

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions