You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
codex-companion task path: missing-cwd misread as 'not installed', prompt fragments parsed as --model (400 as result), dropped turn errors #393
Three of these break the codex:codex-rescue → codex-companion.mjs task path that tools build review/delegation gates on. Found while debugging a downstream pipeline; all three reproduce on v1.0.4. I have small, isolated fixes with node --test coverage and am happy to open a PR if useful.
1. A missing cwd is misreported as "Codex CLI is not installed"
binaryAvailable() (scripts/lib/process.mjs) treats any spawnSyncENOENT as a missing binary. But spawnSync also raises ENOENT when the cwd does not exist. So when a caller passes a cwd that has been removed (e.g. a cleaned-up git worktree in a parallel/fleet runner), getCodexAvailability(cwd) returns "not found" and every entrypoint throws "Codex CLI is not installed or is missing required runtime support" — even though Codex is installed and authenticated.
Fix: a --version/--help probe never depends on the caller's cwd. If a requested cwd doesn't exist, drop it for the probe so availability reflects the binary itself.
2. Prompt fragments are parsed as --model, returning a 400 as the result
The documented forward is codex-companion.mjs task "<raw arguments>" (one quoted string). normalizeArgv() re-splits that single arg with splitRawArgumentString(), and parseArgs() (scripts/lib/args.mjs) then treats any --prefixed token as an option. A prompt that mentions running -m pytest, or a module path like conflict_scan.cli, gets a fragment consumed as --model (alias -m). Codex then 400s — "The 'pytest' model is not supported when using Codex with a ChatGPT account." — and that error envelope is returned as the task/review output.
Repro:task "--write review the conflict_scan.cli module and run -m pytest to verify" → turn started with model: "pytest".
Fix: an opt-in parseArgs option (stopAtFirstPositional), used by handleTask, so option parsing ends once the free-text prompt begins (routing flags precede the prompt). Default behavior unchanged for all other commands.
3. Turn-level errors are dropped from the result status
buildResultStatus() (scripts/lib/codex.mjs) returns success purely on finalTurn.status === "completed", ignoring turnState.error. An app-server error notification can coexist with the inferred/synthetic "completed" turn produced when the main thread drains without a real turn/completed — so an errored run is reported as a clean pass.
Fix: treat any captured turnState.error as failure.
Bonus (docs/contract): silent failures from the rescue subagent
agents/codex-rescue.md / skills/codex-cli-runtime/SKILL.md instruct: "If the Bash call fails or Codex cannot be invoked, return nothing." Callers then can't distinguish a clean "no findings" result from a silent abort. Consider emitting a structured failure envelope so callers can branch deterministically.
Environment: codex-plugin-cc v1.0.4, codex-cli 0.139.0, Node 24, macOS. Happy to send a PR with the fixes + tests.
Follow-up after an independent review of the proposed RC3 fix (the stopAtFirstPositional approach): it closes the common case (prompt fragments after the first positional), but a prompt whose first token is itself flag-like — e.g. a single raw arg that re-splits to --write -m pytest review ... — still consumes -m pytest as --model before any positional appears.
The fully robust fix is to keep the prompt out of the option pipeline entirely: have the rescue forward place the prompt after a -- separator (parseArgs already supports -- passthrough), or pass it via stdin / --prompt-file. stopAtFirstPositional is a good defense-in-depth layer on top, but the --/stdin route is what closes it completely. A model-value allowlist would also catch obvious non-models, but it can't be the only fix since the prompt is corrupted before validation runs.
Still reproduces on v1.0.6 and on current main (db52e28), with codex-cli 0.156.1 on macOS. This adds one more trigger that joins parts 1 and 2 of this issue. On its own it fully breaks review-onlycodex:codex-rescue dispatches.
Trigger: a prompt that contains git -C <path>, which reviewers write all the time ("get the diff via git -C ~/wt diff origin/main...HEAD").
The rescue agent adds --write by default, but leaves it off for review, diagnosis, or research asks. A review-only forward is therefore task "<prompt>", which is exactly one argv token.
normalizeArgv re-splits that token, and parseCommandInput's built-in alias C: "cwd" turns -C ~/wt into --cwd "~/wt".
resolveCommandCwd resolves it to <process.cwd()>/~/wt, because nothing expands the tilde. That directory does not exist.
spawnSync("codex", ["--version"], { cwd }) raises ENOENT. binaryAvailable reports that as "not found", which is part 1 of this issue. The user sees "Codex CLI is not installed or is missing required runtime support", and the real detail is dropped.
Minimal repro (fails in about 80 ms, and codex is never executed):
The same prompt with an absolute existing path does not fail. Instead it silently re-targets the run's cwd and removes -C <path> from the prompt.
Because the error message points at the CLI install, the natural response is to reinstall or upgrade Codex, which doesn't help. We lost time on exactly that, even though codex --version, app-server --help and setup --json all succeeded.
Caller-side workaround until #555 lands: include any real runtime flag, for example --effort medium, so the forward is task --effort medium "<prompt>". Argv then has more than one token, normalizeArgv passes it through, and the prompt stays a single positional. We verified this with a real review-only rescue dispatch that contains git -C ~/….
Two of the fixes discussed here would each have prevented this: #555's -- boundary, and part 1's fix (don't pass the caller's cwd to the --version/--help probe, or at least surface availability.detail in ensureCodexAvailable). It would help if at least the second one could ship on its own, since it turns a misleading "not installed" into an actionable "cwd does not exist".
Activity
CRGDan commentedon Jun 22, 2026
Follow-up after an independent review of the proposed RC3 fix (the
stopAtFirstPositionalapproach): it closes the common case (prompt fragments after the first positional), but a prompt whose first token is itself flag-like — e.g. a single raw arg that re-splits to--write -m pytest review ...— still consumes-m pytestas--modelbefore any positional appears.The fully robust fix is to keep the prompt out of the option pipeline entirely: have the rescue forward place the prompt after a
--separator (parseArgs already supports--passthrough), or pass it via stdin /--prompt-file.stopAtFirstPositionalis a good defense-in-depth layer on top, but the--/stdin route is what closes it completely. A model-value allowlist would also catch obvious non-models, but it can't be the only fix since the prompt is corrupted before validation runs.pablogcloud commentedon Sep 23, 2026
Still reproduces on v1.0.6 and on current
main(db52e28), with codex-cli 0.156.1 on macOS. This adds one more trigger that joins parts 1 and 2 of this issue. On its own it fully breaks review-onlycodex:codex-rescuedispatches.Trigger: a prompt that contains
git -C <path>, which reviewers write all the time ("get the diff viagit -C ~/wt diff origin/main...HEAD").--writeby default, but leaves it off for review, diagnosis, or research asks. A review-only forward is thereforetask "<prompt>", which is exactly one argv token.normalizeArgvre-splits that token, andparseCommandInput's built-in aliasC: "cwd"turns-C ~/wtinto--cwd "~/wt".resolveCommandCwdresolves it to<process.cwd()>/~/wt, because nothing expands the tilde. That directory does not exist.spawnSync("codex", ["--version"], { cwd })raisesENOENT.binaryAvailablereports that as "not found", which is part 1 of this issue. The user sees "Codex CLI is not installed or is missing required runtime support", and the real detail is dropped.Minimal repro (fails in about 80 ms, and codex is never executed):
node scripts/codex-companion.mjs task "Review this. Diff via git -C ~/nope diff origin/main...HEAD"The same prompt with an absolute existing path does not fail. Instead it silently re-targets the run's cwd and removes
-C <path>from the prompt.Because the error message points at the CLI install, the natural response is to reinstall or upgrade Codex, which doesn't help. We lost time on exactly that, even though
codex --version,app-server --helpandsetup --jsonall succeeded.Caller-side workaround until #555 lands: include any real runtime flag, for example
--effort medium, so the forward istask --effort medium "<prompt>". Argv then has more than one token,normalizeArgvpasses it through, and the prompt stays a single positional. We verified this with a real review-only rescue dispatch that containsgit -C ~/….Two of the fixes discussed here would each have prevented this: #555's
--boundary, and part 1's fix (don't pass the caller's cwd to the--version/--helpprobe, or at least surfaceavailability.detailinensureCodexAvailable). It would help if at least the second one could ship on its own, since it turns a misleading "not installed" into an actionable "cwd does not exist".Integrate stock runtime fixes and divergence-aware integration recovery