[CLI][Linux] VS Code Remote-SSH terminal disconnect leaves completed TUI writer locks with no safe recovery

Open 💬 1 comment Opened Aug 19, 2026 by HeiNiao1
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

codex-cli 0.147.0 when the failures were observed. The host is now on the current codex-cli 0.148.0 release. The 0.148.0 release notes do not list a fix for this lifecycle/recovery case, and the 0.148.0 WriterLockCoordinator still returns the same ownerless conflict.

What subscription do you have?

API-key authentication through a Responses-compatible provider; no ChatGPT subscription is involved in this reproduction.

Which model were you using?

gpt-5.6-sol

What platform is your computer?

Linux 5.15.0 x86_64 x86_64 (Ubuntu 22.04.5 LTS)

Node.js v20.20.2, npm 10.8.2, npm-installed Codex CLI.

What terminal emulator and version are you using (if applicable)?

VS Code 1.105.1 Remote-SSH integrated terminal, bash, no tmux/screen/zellij. Codex is launched after sudo su; this host has Defaults use_pty in /etc/sudoers.

Codex doctor report

The full codex doctor --json report was reviewed but is not pasted because it contains private provider URLs and local paths. Relevant sanitized fields after upgrading to 0.148.0 are:

{
  "schemaVersion": 1,
  "codexVersion": "0.148.0",
  "installation": {
    "status": "ok",
    "method": "npm",
    "platform": "linux-x86_64"
  },
  "state": {
    "stateDbIntegrity": "ok",
    "threadHistoryDbIntegrity": "ok",
    "rolloutDbParity": "ok",
    "rolloutScanErrors": 0
  },
  "updates": {
    "latestVersion": "0.148.0",
    "status": "current"
  },
  "note": "The only failing doctor check was TERM=dumb because doctor was captured non-interactively after recovery; it is unrelated to the original VS Code TTY reproduction."
}

What issue are you seeing?

Closing or disconnecting a VS Code Remote-SSH integrated terminal without first entering /quit can leave the standalone Codex TUI process alive behind the sudo su/PTY chain. The latest turn is already complete, but that process continues to hold the per-thread kernel writer lock indefinitely. A later codex resume from a new terminal fails with no owner identity or supported recovery path:

Error: Failed to resume session from /root/.codex/sessions/.../rollout-...-<thread-id>.jsonl:
thread/resume failed during TUI bootstrap:
thread/resume failed: thread <thread-id> already has an active writer (code -32600)

This was independently confirmed for three different threads/workspaces. In each incident:

  • the rollout JSONL parsed completely;
  • the last record was event_msg/task_complete;
  • the rollout had not changed for 16-23 hours;
  • /proc/locks showed FLOCK ADVISORY WRITE on the exact lock-file inode;
  • the owning native Codex PID had open FDs for both the rollout JSONL and thread-writer-locks/<thread-id>.lock;
  • the owner was sleeping in futex_wait_queue_me, with no active turn;
  • one owner had stdin/stdout/stderr on a deleted PTY, while two remained attached to old VS Code PTYs;
  • sending SIGTERM only to the verified native Codex PID released the lock, removed its wrapper/code-mode process family, and made a nonblocking flock probe succeed;
  • the rollout byte size, JSON parse result, final task_complete, and SHA-256 remained unchanged after recovery.

A broader read-only audit on 2026-08-19 found 10 additional old native Codex processes holding 10 distinct thread writer locks, excluding the current live control session:

  • all 10 were sleeping in futex_wait_queue_me;
  • all 10 rollouts ended in task_complete;
  • their rollouts had been unchanged for roughly 2-5 days;
  • four owners had no controlling TTY and six were attached to old PTYs;
  • after npm upgraded the host to 0.148.0, all 10 still executed the deleted 0.147.0 native-binary inode and retained their locks.

The session store itself is healthy. codex doctor reports state DB integrity, thread-history DB integrity, and rollout/DB parity as ok with zero rollout scan errors. This is a live-process ownership/lifecycle problem, not a malformed JSONL or stale empty pathname.

Deleting the empty .lock file is not a safe workaround. The old process still owns an advisory lock on the already-open inode; unlinking the pathname can let a second process create and lock a different inode, violating the single-writer invariant.

What steps can reproduce the bug?

This sequence has reproduced repeatedly on this host:

  1. Connect to a Linux host using VS Code Remote-SSH.
  2. Open an integrated terminal.
  3. Enter sudo su on a host configured with Defaults use_pty.
  4. Start codex in a project directory.
  5. Complete a turn and wait until the TUI is idle at the prompt.
  6. Close/disconnect the VS Code integrated terminal or Remote-SSH client without first entering /quit or /exit in Codex. Depending on the VS Code/sudo PTY lifecycle, the old PTY may remain or become deleted while the Codex process survives.
  7. Open a new VS Code Remote-SSH terminal, enter the same root environment and project directory, and run codex resume or codex resume <thread-id>.
  8. Observe already has an active writer (code -32600) indefinitely.

The conflict can be proven without editing any Codex state:

LOCK=/root/.codex/thread-writer-locks/<thread-id>.lock

stat "$LOCK"                         # record the inode
awk '$0 ~ /<inode>/' /proc/locks     # FLOCK ADVISORY WRITE + owning PID
find /proc/<pid>/fd -lname "$LOCK" -printf '%p -> %l\n'
ps -p <pid> -o pid,ppid,lstart,tty,stat,etime,wchan,args
tail -n 1 /root/.codex/sessions/.../<rollout>.jsonl
flock -n "$LOCK" true               # exits 1 while the old process owns it

Safe manual recovery currently requires identifying the exact owner through the kernel lock and open FDs, verifying its executable/cwd/TTY and completed rollout, sending targeted SIGTERM, and requiring flock -n to return 0 before resuming. Broad pkill, killall, SIGKILL, or lock-file deletion would be unsafe on a host with other live Codex sessions.

What is the expected behavior?

Please preserve the single-writer safety invariant, but make ownership lifecycle and recovery usable:

  1. When a standalone TUI loses its real terminal/client and has no running turn, it should shut down cleanly or release/unload the thread writer after an appropriate grace period.
  2. If a live process still owns the writer, the conflict should identify enough sanitized owner metadata to recover safely: surface/runtime, PID where applicable, process start time, TTY, acquisition time, last thread activity, and whether a turn is running or complete.
  3. Provide a supported, confirmation-gated handoff/recovery command, for example codex resume --takeover <thread-id> or codex sessions locks, which validates PID identity and owner activity, asks the owner to release/exit, and only acquires the writer after the original kernel lock is gone.
  4. Offer read-only inspection or fork as explicit alternatives when takeover is not safe.
  5. Extend codex doctor to report completed threads held for an unusually long time by processes with no TTY, a deleted executable, or a disconnected client.

This should not be implemented by blindly timing out or unlinking a held lock. A lease/owner record with a nonce or epoch, heartbeat/last-activity metadata, and a coordinated release path would retain fencing against late writes.

Additional information

The 0.148.0 implementation in codex-rs/thread-store/src/local/writer_lock.rs explains the current behavior:

  • on TryLockError::WouldBlock, it returns only thread <id> already has an active writer;
  • the lock file contains no owner/recovery metadata;
  • startup cleanup removes only lock paths that can already be locked, so it correctly cannot recover a lock held by an orphaned-but-live process.

The npm wrapper registers SIGHUP forwarding, so this report does not claim that Codex ignores a SIGHUP it actually receives. With VS Code Remote-SSH plus nested sudo use_pty, the evidence only establishes that the TUI can survive the apparent terminal close/disconnect and no supported recovery exists afterward.

Related but distinct reports:

  • #37403: Desktop/Remote/CLI cross-surface writer handoff.
  • #37552: intentionally opening the same thread concurrently on multiple devices.
  • #37856: stale VS Code extension app-server ownership after renderer disconnect.

This report concerns a standalone CLI TUI process left behind by the VS Code Remote-SSH integrated-terminal/PTY lifecycle. No Desktop app, Remote Control daemon, or VS Code Codex extension app-server is required.

Privacy: hostnames, usernames, IP addresses, private project paths/names, provider URLs, account data, full thread IDs, conversation contents, and raw logs are intentionally omitted. Sanitized process/lock evidence can be provided if maintainers need it.

View original on GitHub ↗

1 Comment

github-actions[bot] contributor · 9 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #38876
  • #37969
  • #38297

Powered by Codex Action