app-server + external exec-server: `apply_patch` delete never reaches the environment's `fs/remove` — deletes hit the host-local filesystem and fail

Open 💬 0 comments Opened Jul 17, 2026 by rodja

Summary

Running codex app-server (codex-cli 0.144.3) with an external sandbox
exec-server environment (sandboxPolicy: {type: "externalSandbox"},
environments: [{environmentId: …}]), apply_patch Add/Update hunks are
correctly routed through the environment's fs/writeFile
, but Delete
hunks never emit any fs/* request
. The delete is attempted against the
app-server host's local filesystem instead, where the environment-internal
path does not exist, and fails with Failed to delete file <path>.

Possibly related: #18534 reports the same symptom (Add/Update succeed, Delete
fails with Failed to delete file) on plain CLI, without the wire-level
analysis below.

Environment

  • codex-cli 0.144.3 (@openai/codex-linux-x64, x86_64-unknown-linux-musl)
  • app-server via stdio, code mode active (session/execute freeform

apply_patch tool through codex-code-mode-host)

  • External exec-server environment implementing the documented protocol

(fs/readFile, fs/writeFile, fs/remove, fs/getMetadata, process/*)

  • Embedder: OpenClaw gateway; environment maps /workspace into a Docker

container. The app-server host has no /workspace.

Reproduction

  1. Register an external exec-server environment whose filesystem root (e.g.

/workspace) exists only inside the environment, not on the app-server host.

  1. Prompt a turn to delete an existing file via apply_patch

(*** Delete File: /workspace/tmp/x/DELETE_ME.md).

  1. Observe (syscall trace of all write/writev/sendmsg on the app-server

process tree): zero fs/* requests are sent for the delete; the tool call
fails with exit code 1 and output Failed to delete file /workspace/tmp/x/DELETE_ME.md.

  1. Control: an Add or Update hunk on the same path in the same session emits

fs/writeFile and succeeds inside the environment.

Expected

Delete hunks should use the same ExecutorFileSystem bridging as writes —
codex-rs/apply-patch/src/lib.rs (rust-v0.144.3) calls
ensure_not_directoryfs.get_metadata(...) and fs.remove(...,
RemoveOptions { recursive: false, force: false }, sandbox)
, so the
implementation clearly intends to route through the abstraction; in the
app-server + external-environment constellation those calls never reach the
environment.

Impact

Every apply_patch delete in an externally sandboxed embedding fails; models
fall back to shell rm. Confirmed on two independent turns with full-tree
syscall traces; the embedder's fs/remove handler was verified working by
driving it directly with the exact recursive:false, force:false options.

View original on GitHub ↗