DEV Community

SimonLi
SimonLi

Posted on

How to Fix `already has an active writer` in Codex CLI

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)
Enter fullscreen mode Exit fullscreen mode

This guide provides a direct troubleshooting flow:

  1. Install strace.
  2. Identify the suspicious process associated with a specific JSONL session.
  3. If individual diagnosis is unnecessary, clean up all leftover codex resume --all processes.

Important: Do not delete the session .jsonl file 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Verify the installation:

strace -V
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The thread ID that can be used for matching is:

01a0c1ca-74cd-7d21-bd7b-1de7e03f7f96
Enter fullscreen mode Exit fullscreen mode

3.1 List all suspicious Codex PIDs

First, list all currently running Codex processes:

ps auxww | grep '[c]odex'
Enter fullscreen mode Exit fullscreen mode

If the issue is likely caused by a bulk-resume task, narrow the list to:

ps auxww | grep '[c]odex resume --all'
Enter fullscreen mode Exit fullscreen mode

The second column in the output is the PID. For example:

root  100621  ...  codex resume --all --include-non-interactive
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Try a graceful termination first:

kill <PID>
Enter fullscreen mode Exit fullscreen mode

Wait three seconds and confirm whether it has exited:

sleep 3
ps -p <PID> -o pid,stat,etime,cmd
Enter fullscreen mode Exit fullscreen mode

If it still exists, use a forced termination:

kill -9 <PID>
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Wait a few seconds and check again:

sleep 3
ps auxww | grep '[c]odex resume --all'
Enter fullscreen mode Exit fullscreen mode

5.3 Use SIGKILL only for surviving processes

ps auxww | grep '[c]odex resume --all' | awk '{print $2}' | xargs -r kill -9
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

6. Resume the session again

After the stale writer process has been cleaned up, retry:

codex resume
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then investigate Codex instances in other environments, confirm the shared-storage path, and check the installed Codex version.

Top comments (0)