You lose the SSH connection mid turn, come back, and run codex resume. Instead of the conversation, you get one flat line: thread 019a4c1e-... already has an active writer. The thread is not corrupted and nothing was lost. Codex is only telling you that another process still holds the right to write to it.
This is a coordination error, not an account or model error. It has a known cause, an identifiable owner, and a command to fix it. Below is the mechanism, how to find the process holding the thread, and what to do when that process lives on another machine.
What does "already has an active writer" mean?
Codex allows a single writer per thread, and that writer is enforced by an operating system file lock. Every thread has its own: $CODEX_HOME/thread-writer-locks/<thread_id>.lock, with ~/.codex as the default when CODEX_HOME is not set.
The lock attempt is non blocking. Whoever arrives second does not wait its turn, it gets the error right away:
thread 019a4c1e-... already has an active writer
The path is explicit in the source: the process calls try_lock on the file, and if the result is WouldBlock it returns exactly that message (codex-rs/rollout/src/writer_lock.rs). It is not a network timeout, not quota, not a login problem. It is a local lock that someone is still holding.
Why is the lock still held after I close the terminal?
Because the process did not die. Closing the terminal window, losing the SSH connection, or suspending with Ctrl+Z does not terminate Codex, and the lock is only released when the process actually ends.
| Situation | Who keeps the thread | Where it is reported |
|---|---|---|
| SSH drop with an idle TUI | The TUI process left orphaned on the server | openai/codex #44063 |
| Thread open in the desktop app and the CLI | The desktop app, which opened the thread first | openai/codex #37403 |
| Thread viewed in the iOS app via Remote Control | The app-server daemon serving Remote Control | openai/codex #44449 |
All three cases share the same shape: an old session nobody closed. None of them requires reinstalling anything.
How do I find which process is holding the thread?
The thread id is in the error message itself. With it, point lsof at the matching lock file:
lsof ~/.codex/thread-writer-locks/<thread_id>.lock
The output gives you the PID in the second column:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 48213 mafra 23u REG 0,45 0 912 /home/mafra/.codex/thread-writer-locks/019a4c1e-....lock
If your CODEX_HOME points elsewhere, swap the path for $CODEX_HOME/thread-writer-locks/. fuser -v on the same file gives the same answer when lsof is not installed.
How do I release the thread without losing the history?
Stop the process holding the lock and resume the session:
kill 48213
codex resume 019a4c1e-...
You do not need to delete the .lock file. The lock belongs to the operating system and is released the instant the process ends. Deleting the file by hand creates a second problem: the old process stays alive writing to the same thread, so you end up with two writers in the same rollout, which is exactly what the lock exists to prevent.
If the lock owner is the app-server daemon, use its own command instead of kill:
codex app-server daemon stop
What if the process is on another machine?
Then you cannot kill anything. What you can do is read the thread: in recent TUI versions, when resume hits the lock, the conversation opens in read-only mode with the input box disabled, instead of simply failing.
In that mode, the f key creates a copy of the thread without asking for the original lock. The copy gets a new id, brings the history along, and accepts writes normally. That is the way to keep working when the machine holding the thread is not within your reach.
Worth knowing what it costs: the original thread stays locked, and the copy becomes the live conversation. If you later regain access to that machine, stop the old process so you do not end up with two diverging history lines.
How do I keep the lock from freezing the thread again?
One thread, one client. The lock exists because two surfaces writing to the same conversation produce inconsistent history, so the practical rule is to never open the same thread in two places at once.
- Before resuming in the CLI, close the desktop app that had the thread open.
- When leaving a remote session, exit Codex with
/exitbefore dropping SSH. Closing the terminal does not terminate the process. - For parallel work, use a copy of the thread instead of two windows on the same session.
- If you use Remote Control, check whether the thread is still open in the phone app before resuming on the desktop.
Quick questions
Does deleting the .lock file fix it?
No. The lock belongs to the process, not the file. Deleting the file while the owner is still alive leaves two writers on the same thread, which is the problem the lock prevents.
The error shows up on one thread only and the others work. Why?
Because the lock is per thread, not per installation. An orphaned session freezes only that conversation, and the rest of Codex keeps working.
Is this a bug in my setup?
Not necessarily. The reports open in the repository show the same behavior on Linux with SSH, on macOS with desktop and CLI, and on Remote Control with iOS. It is thread coordination across surfaces, not broken configuration.
If the reason you keep the same thread in three clients is taking turns to avoid blowing through a quota, the shortest path is a single CLI. Verboo Code delivers unlimited tokens and switches models with /model, with no per-token billing, so there is no reason to keep two clients on the same session. Meet Verboo Code.



