Codex sandbox_permissions approval doesn't get surfaced through app-server

Open 💬 4 comments Opened May 9, 2026 by amirbarak
💡 Likely answer: A maintainer (etraut-openai, contributor) responded on this thread — see the highlighted reply below.

What issue are you seeing?

While integrating Codex through codex app-server, I saw a codex turn stall on a permission/escalation prompt that appeared in the codex session transcript, but no matching app-server item/*/requestApproval server request over JSON-RPC.

This leaves app-server clients unable to display or resolve the approval. The turn appears active but cannot progress until the hidden approval times out.

  • Codex CLI: 0.130.0
  • Mode: codex app-server --listen stdio://
  • Approval policy: on-request
  • Sandbox: workspace-write
  • Platform: Linux

What steps can reproduce the bug?

  1. Start app-server:

codex app-server --listen stdio:// \
-c 'model="gpt-5.5"' \
-c 'approval_policy="on-request"' \
-c 'thread_sandbox="workspace-write"' \
-c 'turn_sandbox_policy="workspace-write"'

  1. Initialize app-server over stdio.
  2. Start a thread in a git worktree whose .git metadata lives outside the sandbox writable roots.
  3. Start a turn asking Codex to stage/commit changes, or otherwise run a command that requires sandbox escalation, for example git add .... This should trigger codex for sandbox_permissions approval.
  4. Observe the app-server JSON-RPC stream.
  5. Compare it with the saved Codex session transcript under ~/.codex/sessions/....

What is the expected behavior?

I expect app-server to surface a item/*/requestApproval as with other approval requests.

Additional information

Here's what it looked like for me:

session transcript:

{
"type": "function_call",
"name": "exec_command",
"arguments": {
"cmd": "git add foobar",
"workdir": "/tmp/testbranch1",
"yield_time_ms": 1000,
"max_output_tokens": 12000,
"sandbox_permissions": "require_escalated",
"justification": "Allow staging the test file in this git worktree; the git metadata lives under /home/... outside the writable sandbox.",
"prefix_rule": ["git", "add"]
}
}

After 300 seconds, the session transcript recorded:

{
"type": "function_call_output",
"output": "aborted by user after 300.0s"
}

But the app-server client never received an approval request it could answer.

View original on GitHub ↗

4 Comments

clucluclu · 3 months ago

Just leaving a note here as I am absolutely invested in the outcome for this - without it only options within paseo are full access permissions, which is too unsafe to be able to use in my case..really keen to see this one resolved. Linux daemon and codex server runtime .130 - win 11 and ios paseo clients latest as of today).

etraut-openai contributor · 3 months ago

Thanks for the detailed report. I investigated this path and I don’t think this is an app-server bug.

The important detail is that approval_policy = "on-request" is not, by itself, enough to guarantee that escalation requests are surfaced to the app-server client as item/commandExecution/requestApproval. If the session is configured with an automatic approvals reviewer, such as guardian_subagent / auto-review, the escalation is handled by that reviewer instead of being routed to the client for manual approval.

I was able to exercise the repro shape directly: app-server over stdio, workspace-write, on-request, a git worktree whose .git metadata points outside the writable root, and an exec_command call with sandbox_permissions = "require_escalated". When approvals_reviewer = "user" was set, app-server correctly emitted item/commandExecution/requestApproval.

So the behavior described here is most likely explained by the session using an automatic approvals reviewer, not by app-server dropping the approval request.

If you still believe this is a bug, please provide a raw JSON-RPC trace that includes the thread/start response, especially the approvalsReviewer value, plus the subsequent turn events. Without that, this looks like a configuration/client expectation issue rather than a Codex defect.

amirbarak · 3 months ago

Hi @etraut-openai, thanks for looking into this!

Edit: just noticed you asked for the subsequent turn event logs as well, my bad. I'm attaching PII-redacted logs here:

Codex:
codex-session-transcript-excerpt-public-redacted.json

App-server:

app-server-full-stream-public-redacted.json

Main things I'm seeing from these two logs:

  1. The Codex transcript has a normal git add <changed-files> call that fails with:

fatal: Unable to create '/repo/.git/worktrees/<worktree>/index.lock': Read-only file system

  1. The same transcript then records an escalated exec_command call with:
  • sandbox_permissions: "require_escalated"
  • prefix_rule: ["git", "add"]
  • a user-facing approval justification
  1. The app-server stdout JSON-RPC stream has no requestApproval entry and no waitingOnApproval status transition.
  2. The full app-server stream includes stderr:

failed to record rollout items: thread <opaque-id> not found

  1. The full app-server stream then terminates after the external 300s no-activity timeout.
  2. approvalsReviewer is set to "user".

Thanks again!

Necmttn · 2 months ago

Even if an automatic reviewer handles an escalation, app-server clients still need an event for the pending state.

I would stream an approval lifecycle item with correlation id, command/tool call id, sandbox policy, requested permission, reviewer route (client vs auto), final decision, and timeout/error state. Then clients can render handled by auto reviewer instead of looking stalled with no resolvable approval.

---

_Generated with ax._