Resume a Codex session, and get past "already has an active writer"
A Codex session is a saved chat: the messages, the tool calls, their output, and the directory it was working in. Codex writes it to disk as you go, so closing the terminal does not delete it. The transcript lives under your Codex home, ~/.codex, unless CODEX_HOME moves it.
Most "my session is gone" cases are not gone. You are looking from a different directory, surface, or provider. The other failure is the opposite: the session is there, but another surface still owns it. Codex allows one writer per chat and refuses a second.
For the active-writer error: close or quit the surface that owns the chat. If you cannot, /fork it. The section "Already has an active writer" explains the lock and the exceptions.
Checked against Codex's documentation on 26–27 October 2026.
Status, 26 October 2026: the "already has an active writer" error is not documented in the Codex pages checked for this guide, but it is a well-documented pattern in the issue tracker. One open report covers the desktop and CLI fighting over a Remote Control chat (#37403, 67 comments, updated 22 October 2026); another covers a chat left locked by the mobile app (#44449, updated 19 October 2026); a third covers a lock that survived after the desktop app exited (#49137, updated 1 October 2026). The ownership rule and the way out are below.
What "session" means in Codex#
Codex uses the word *thread* for the stored conversation object in its runtime and API. In the CLI, the desktop app, and the IDE extension, you see it as a chat. A chat remembers its transcript and the working directory it was started in. That recorded directory matters more than most people expect, because resume uses it to decide where the chat belongs.
The commands people mix up#
| You want | Command | What it does |
|---|---|---|
| Pick from your recent chats | codex resume | Opens the session picker. |
| Reopen the last chat here | codex resume --last | Picks the most recent session from the current working directory. |
| Reopen the last chat anywhere | codex resume --last --all | Widens --last past the current directory. |
| Go straight to a chat you have an ID for | codex resume <session-id> | Resumes that session. |
| Pick from inside a running chat | /resume | Opens the saved-chat picker without leaving the terminal session. |
| Branch instead of taking over | /fork or codex fork | Copies the chat into a new thread with a fresh ID; the original stays untouched. |
| Remove it from the list, keep the file | /archive | Archives the session; codex unarchive <session> brings it back. |
| Delete it for good | /delete | Deletes the transcript and any spawned descendant sessions. |
--last is the trap in that table. It is scoped to the current working directory, not the machine. If you opened the terminal somewhere else, it will resume the newest chat saved there, which may not be the one you meant. Add --all when you mean "anywhere".
/new starts a fresh chat in the same CLI process; /clear clears the visible transcript and starts fresh. Neither deletes the old session.
Where Codex stores sessions, and why old ones disappear#
Codex keeps local state under CODEX_HOME, default ~/.codex. The troubleshooting page puts session transcripts in $CODEX_HOME/sessions and archived sessions in $CODEX_HOME/archived_sessions. CODEX_HOME moves the configuration, the session store, and the writer locks together, so a Codex launched with a different home sees a different set of chats. The writer locks are the files that keep one writer per chat; they live under CODEX_HOME/thread-writer-locks/ (#38150, open, updated 16 October 2026).
Codex calls a saved transcript a rollout file. The hooks documentation gives a transcript path ending in .jsonl (JSONL: a text file with one JSON object per line), and codex exec --ephemeral runs without persisting rollout files to disk. Treat the sessions/ directory as the transcript store. The Codex docs are inconsistent here: advanced configuration calls history.jsonl a session transcript, while troubleshooting treats sessions/ as the transcript store and history.jsonl as prompt history. In practice, use sessions/ for transcripts; history.jsonl is the prompt-history file, used for up-arrow recall and Ctrl+R search, and history.max_bytes caps and prunes it, not the session store.
The Codex docs checked for this guide describe no age-based cleanup. A session stays on disk until you delete it; archiving only hides it:
codex archive(or/archive) removes it from the active picker but keeps the transcript; archived sessions are stored underarchived_sessions, andcodex unarchivebrings one back.codex delete(or/delete) permanently removes the transcript and any spawned descendant sessions.codex exec --ephemeralwrites no rollout file for that run.[history] persistence = "none"stopshistory.jsonl; it does not delete the session store.
The sections below cover the other ways a chat leaves the list: the wrong directory, another surface, a provider switch, or a different Codex home. If the transcript is gone, only a copy brings it back. Export a Claude Code conversation covers a readable export for all three tools, back up coding-agent sessions covers the copy, and delete session history covers the delete.
Start with the directory#
Resume cares where you are standing. If the current working directory differs from the session's saved directory, Codex asks which one to use. You can answer that question once per run, or set a default:
# ~/.codex/config.toml
[tui]
resume_cwd = "session" # or "current"The configuration reference lists session and current as the two values but does not define them; the usual reading is that session keeps the chat in the directory where it started and current opens it in the directory you are in now. An explicit --cd <directory> (-C) overrides both. If you resume from a worktree — a second checkout of the same repository — and wonder why the chat is looking at the main checkout, this setting is the first thing to check.
The CLI treats the directory where you start it as the project. The IDE extension uses the open folder or workspace root. The desktop app uses a local project's primary folder for the chat's files and for AGENTS.md.
When it is not in the picker#
Archive and delete are covered in the section above.
It belongs to another surface#
codex resume lists local chats for the CLI. The desktop app and the IDE extension keep their own lists. The desktop app does index CLI chats by project folder: add the folder the chat started in as a project, and it should appear. That closed the request for CLI history in the desktop app (#21079, completed 5 August 2026), though a comment on 18 September 2026 says the Linux app still misses them. If it does not appear, resume it in the CLI and use /app on macOS or Windows to continue in the desktop app.
The provider changed#
A session is tied to the provider that wrote it — the service that serves the model, such as OpenAI or a custom gateway. If you switch model_provider, the default resume path can stop offering local sessions from the old provider. The maintainers closed this as intended: switching providers inside one session is not a supported happy path, and codex resume --all reaches the older sessions if you accept the risk (#15494, closed as not planned 31 March 2026). Put the old provider back first.
It is under a different Codex home#
CODEX_HOME moves the configuration, the session store, and the writer locks together. A chat started with a custom home is invisible to a Codex launched without it. Run echo $CODEX_HOME in the shell that starts Codex and compare. This is also the WSL trap: Windows Subsystem for Linux (WSL) lets a Windows desktop app run against a Linux environment, and that app can inherit the Windows Codex home, so the CLI inside WSL and the app outside it see different sessions (#13762, open, updated 19 September 2026).
"Already has an active writer"#
This error means the thread is open somewhere else. Codex allows one writer per thread so two surfaces cannot interleave edits into one transcript. The lock is held by a running process, not a copy of the conversation. The transcript is usually fine, which is why the same session opens after you close the surface that owned it.
The error looks like this:
thread/resume failed: thread <thread-id> already has an active writer (code -32600)The owner can be a terminal you forgot about, a tab in the desktop app, a chat in the IDE, a mobile Remote Control session (steering the chat from your phone), or a background app-server (the local helper the desktop app uses to run chats) left running after its window closed. Closing the chat is not always enough; quitting the surface normally releases the lock.
Try these:
- Close the chat on the other surface. Close the tab; if that fails, quit the desktop app fully. On a phone, leave the Remote Control view. In the IDE, close the chat or reload the window.
- Check for a terminal you left running. An idle terminal session still holds the chat, and after an SSH disconnect it can survive for hours (#44063, open, updated 18 September 2026). Exit that session, or end the leftover Codex process.
- Check for an orphaned app-server. A desktop app that exited can leave its helper alive, and the lock with it (#49137). Quit every Codex surface; if that fails, end the orphaned process or reboot.
- Fork instead of taking over.
/forkorcodex fork <session-id>copies the chat into a new thread. You get the history and a fresh writer, and the original stays with its owner. - Delete the lock only as a last resort. Locks live under
CODEX_HOME/thread-writer-locks/, one per thread plus a coordination lock (#38150, open, updated 16 October 2026). A lock whose process is gone is stale; a live one is doing its job, and deleting it lets two surfaces write the same transcript. Back up the session file first.
If the error names mcp_servers.codex_app on a WSL-hosted thread, it is a different open bug, not a lock you can clear by closing a tab (#40819, open, updated 30 August 2026).
Another machine, and another tool#
The docs describe no sync of the local Codex session store between machines. Those files are local under the Codex home; copying that home between machines is not a documented handoff. The Codex CLI's /export writes the current chat as Markdown, a copy for reading that Codex cannot resume from; export a Claude Code conversation covers it alongside Claude Code and OpenCode. To copy the session files, see back up coding-agent sessions.
If you need the work to continue elsewhere, write a short handoff file with the goal, decisions, failed attempts, open items, and changed files for the next session to read. The handoff guide covers that shape, and syncing agent memory covers shared stores. Resume the work, not the process.
Check you got the right chat#
Before you continue, ask for the last decision you made together, the current branch and commit, and the next action. Compare them with git status and git log -1. If the chat was compacted — older turns replaced by a model-written summary — ask it to quote the constraint you care about, not to summarise it. A summary keeps the shape of the work and can drop the exact command. /recap summarises on demand without compacting; it is useful for orientation, not proof.
Where to go next#
- Export a Claude Code conversation — the readable export, including the Codex and OpenCode commands.
- Back up coding-agent sessions — a copy that survives a delete or a moved home.
- Delete session history — remove sessions on purpose, including what the vendor keeps.
- Codex guides — sessions, permissions, hooks, and config.
- Why Codex ignores part of your AGENTS.md — if the resumed chat is missing its rules.
- Context compaction — if it lost the middle of the work.
- Hand off work between Claude Code and Codex — if the work has to move machines.