TUI fork keeps the parent thread's writer active, preventing resume from another TUI
Codex version
codex-cli 0.147.0
### Environment
- macOS 26.4.1 (25E253)
- Apple Silicon (arm64)
- zsh 5.9
- Installed via
@openai/codex
### Description
After forking an active thread from the Codex TUI, the original parent thread remains owned by the same TUI/app-server process as an active writer.
As a result, attempting to resume the parent thread from another TUI fails with:
```text
Failed to resume session from ~/.codex/sessions/.../rollout-<timestamp>-<parent-thread-id>.jsonl: thread/resume
failed during TUI bootstrap: thread/resume failed: thread <parent-thread-id> already has an active writer (code -32600)
The fork itself succeeds and the child thread is usable. However, forking does not appear to release or unload the parent thread's writer.
### Steps to reproduce
- Start Codex TUI and create or resume a thread.
- Complete a turn in the parent thread.
- Use the TUI fork flow to fork it into a new thread.
- Continue working in the forked child thread.
- Keep the original TUI process running.
- From another terminal, run:
codex resume <parent-thread-id>
- Observe that resume fails because the parent thread still has an active writer.
### Expected behavior
After the TUI switches to the forked child thread and no turn is running on the parent, the parent thread's writer should be released so that another Codex
client can resume it.
If retaining ownership is intentional, the UI should make this behavior clear and ideally provide a way to explicitly unload/release the parent thread.
### Actual behavior
The parent remains locked for the lifetime of the original TUI process, even though the active work has moved to the forked child.
The parent becomes resumable only after terminating the original TUI process.
### Diagnostics
The child rollout metadata confirms that this was a real fork:
{
"originator": "codex-tui",
"source": "cli",
"history_mode": "paginated",
"forked_from_id": "<parent-thread-id>",
"history_base": {
"thread_id": "<parent-thread-id>",
"end_ordinal_exclusive": 286,
"end_byte_offset": 4185791
}
}
While the error was occurring, lsof showed that the original Codex TUI process still had the parent rollout file open for writing:
codex <pid> ... 61u REG ... ~/.codex/sessions/.../rollout-...-<parent-thread-id>.jsonl
The process holding the file was the original codex-tui process attached to the first terminal. A separate local app-server process was checked and had no
rollout files open.
After terminating only the original TUI process, lsof reported no holder and codex resume <parent-thread-id> succeeded.
### Additional context
The forked thread uses paginated history backed by the parent rollout. Keeping the parent available as a read-only history base may be necessary, but retaining
its active writer appears unnecessary and prevents legitimate resume operations from another client.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Confirmed by inspection on
main@ 1f41cc5d92: the TUI fork/switch path just swaps the active thread and chat widget —session_lifecycle.rs#L520-L560setsactive_thread_id, replaces the widget, replays the child snapshot — and never closes/unloads the parent thread. The parent stays loaded in the app-server as a cache entry with its rollout writer still held, which is exactly what the resume error names (thread <id> already has an active writer).Two fix levels:
thread_processor.rs(see #39086's analysis of that path).Either way, the child-works/parent-stranded asymmetry you hit is the loaded-thread cache holding resources it no longer needs.
Hi @zeyozh, the frozen history/resume behavior you described is one of the persistence patterns I've been studying. Codex Rescue Alpha5 includes a read-only projection consistency check that compares canonical rollout progress against stored projection cursors without touching SQLite.
If you still have the affected pre-repair session locally, you can test it with:
(or point
doctordirectly to the rollout file). Please sanitize/redact any sensitive paths or names before sharing diagnostic output; raw session files are never needed.