Fixing already has an active writer When Resuming a Codex CLI Session
When restoring a historical session in Codex CLI, you may see:
thread/resume failed during TUI bootstrap:
thread <THREAD_ID> already has an active writer (code -32600)
This guide provides a direct troubleshooting flow:
- Install
strace. - Identify the suspicious process associated with a specific JSONL session.
- If individual diagnosis is unnecessary, clean up all leftover
codex resume --allprocesses.
Important: Do not delete the session
.jsonlfile as a first response. The error usually means another process still owns the thread writer, not that the session file itself is corrupted.
1. Cause: a single-writer lock per thread
The already has an active writer error is not necessarily caused directly by a disconnected SSH session or a closed terminal. The underlying issue is that Codex enforces a single-writer constraint for each thread.
Codex continuously appends events to a session rollout .jsonl file. To prevent multiple processes from writing to the same thread at the same timeโwhich could cause out-of-order events, duplicated entries, or inconsistent session historyโCodex maintains writer ownership or a writer lock for each thread.
A new thread/resume request must obtain that writer lock before it can continue. Therefore, the error means:
The current resume request detected that writer ownership for this thread still belongs to another Codex instance or background writer task, so it refused to start a second writer.
2. Install strace on Linux
strace can help determine whether a process is accessing the target session file or making file-lock-related system calls.
First, identify your Linux distribution:
cat /etc/os-release
Install strace based on the distribution:
# Debian / Ubuntu
apt-get update && apt-get install -y strace
# Alpine
apk add --no-cache strace
# CentOS / RHEL / Rocky Linux / AlmaLinux
dnf install -y strace
# Older RHEL/CentOS systems
yum install -y strace
Verify the installation:
strace -V
3. Find the process for a specific JSONL session
Assume the error includes this session file:
/dfs/data/.codex-data/sessions/2026/09/21/rollout-2026-09-21T10-27-46-01a0c1ca-74cd-7d21-bd7b-1de7e03f7f96.jsonl
The thread ID that can be used for matching is:
01a0c1ca-74cd-7d21-bd7b-1de7e03f7f96
3.1 List all suspicious Codex PIDs
First, list all currently running Codex processes:
ps auxww | grep '[c]odex'
If the issue is likely caused by a bulk-resume task, narrow the list to:
ps auxww | grep '[c]odex resume --all'
The second column in the output is the PID. For example:
root 100621 ... codex resume --all --include-non-interactive
In this example, 100621 is the PID.
3.2 Trace one PID with strace
Replace <PID> with the real process ID, for example 100621:
strace -f -tt -s 256 -p 100621 \
-e trace=%file,flock,fcntl,read,write \
2>&1 | tee /tmp/codex-100621.strace
| Option | Purpose |
|---|---|
-p 100621 |
Attaches to an existing process |
-f |
Also traces child processes and threads |
-tt |
Prints precise timestamps |
-s 256 |
Prints up to 256 characters, avoiding truncated paths |
-e trace=%file,flock,fcntl,read,write |
Limits output to file access, locks, reads, and writes |
tee |
Writes output to both the terminal and a log file |
Keep strace running. In another terminal, try codex resume again to trigger activity. Observe the output for a few seconds, then press Ctrl+C to stop strace.
Pressing Ctrl+C stops only strace; it does not terminate the Codex process being traced.
Search whether the PID accessed the target JSONL file or processed a lock:
grep -Ei '01a0c1ca-74cd-7d21-bd7b-1de7e03f7f96|writer|lock|\.jsonl' \
/tmp/codex-100621.strace
If the output contains the complete target .jsonl path, or lock-related calls such as flock, fcntl, or F_SETLK, that PID is actively handling the session and should be treated as a priority investigation target.
3.3 Check every resume --all PID in bulk
If you have several suspicious processes, briefly trace each codex resume --all process and search for the target thread ID:
THREAD_ID='01a0c1ca-74cd-7d21-bd7b-1de7e03f7f96'
for pid in $(ps auxww | grep '[c]odex resume --all' | awk '{print $2}'); do
echo "Checking PID: $pid"
strace -f -tt -s 256 -p "$pid" \
-e trace=%file,flock,fcntl \
-o "/tmp/codex-$pid.strace" &
trace_pid=$!
sleep 3
kill "$trace_pid" 2>/dev/null || true
if grep -Eqi "$THREAD_ID|writer|lock|\.jsonl" "/tmp/codex-$pid.strace"; then
echo "Possible match: PID $pid"
grep -Ei "$THREAD_ID|writer|lock|\.jsonl" \
"/tmp/codex-$pid.strace" | tail -n 20
fi
done
A process that does not make a filesystem call during the three-second tracing window is not necessarily unrelated to the target thread. It may simply be idle or waiting. This script is intended to reduce the search space, not replace judgment about the process state and workload.
4. Terminate the process associated with one JSONL session
After confirming that a PID is not performing valid work and that it accesses or owns the target thread, inspect its full details first:
ps -fp <PID>
Try a graceful termination first:
kill <PID>
Wait three seconds and confirm whether it has exited:
sleep 3
ps -p <PID> -o pid,stat,etime,cmd
If it still exists, use a forced termination:
kill -9 <PID>
A plain kill sends SIGTERM, allowing the process an opportunity to release resources and run shutdown cleanup. kill -9 sends SIGKILL, which cannot be caught or handled; the process stops immediately and cannot perform cleanup. Use kill -9 only for a stuck process after you have confirmed that it is safe to terminate.
5. Terminate all codex resume --all processes
If you have confirmed that all codex resume --all --include-non-interactive processes are stale and no longer have business value, you can clean them up together.
5.1 Preview the processes first
ps auxww | grep '[c]odex resume --all'
Carefully check the START, TIME, TTY, and COMMAND columns. Do not terminate a session that was started recently and is still performing real work.
5.2 Send SIGTERM first
ps auxww | grep '[c]odex resume --all' | awk '{print $2}' | xargs -r kill
Wait a few seconds and check again:
sleep 3
ps auxww | grep '[c]odex resume --all'
5.3 Use SIGKILL only for surviving processes
ps auxww | grep '[c]odex resume --all' | awk '{print $2}' | xargs -r kill -9
The following script can be saved as kill_codex_resume_all.sh. It displays the target processes and asks for confirmation before sending SIGTERM. Passing -9 sends SIGKILL instead.
#!/usr/bin/env bash
PATTERN='[c]odex resume --all'
SIGNAL='TERM'
[ "${1:-}" = '-9' ] && SIGNAL='KILL'
PIDS=$(ps auxww | grep "$PATTERN" | awk '{print $2}')
if [ -z "$PIDS" ]; then
echo 'No codex resume --all processes found'
exit 0
fi
ps auxww | grep "$PATTERN"
printf '\nSend SIG%s to the PIDs above? [y/N] ' "$SIGNAL"
read -r answer
case "$answer" in
y|Y) ;;
*) echo 'Cancelled'; exit 0 ;;
esac
for pid in $PIDS; do
kill -s "$SIGNAL" "$pid" && echo "Sent SIG$SIGNAL to PID: $pid"
done
Make the script executable and run it:
chmod +x kill_codex_resume_all.sh
# Default: send SIGTERM
./kill_codex_resume_all.sh
# Use SIGKILL only if normal termination did not work
./kill_codex_resume_all.sh -9
6. Resume the session again
After the stale writer process has been cleaned up, retry:
codex resume
If already has an active writer still appears, do not delete the session .jsonl file. At that point, one of the following is more likely:
- The writer is still active on another machine, container, or Codex client.
- Internal writer state on shared storage was not released normally.
- Your current Codex version has a writer-ownership or app-server issue.
As a temporary workaround, start a new session without resume:
codex
Then investigate Codex instances in other environments, confirm the shared-storage path, and check the installed Codex version.
Top comments (0)