Session disappear. Codex trips over bad JSON history.
Open 💬 8 comments Opened May 25, 2026 by nialse
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
What version of Codex CLI is running?
codex-cli 0.133.0
What subscription do you have?
Pro
Which model were you using?
gpt-5.5
What platform is your computer?
Linux 6.8.0-117-generic x86_64 x86_64 over ssh from Codex App on MacOS
What terminal emulator and version are you using (if applicable)?
_No response_
Codex doctor report
What issue are you seeing?
Codex App thread disappeared. Session was not listed in codex on the host accessed over SSH either.
Cause: JSON for history was broken, did not validate. Works after removing offending and broken parts of the JSON.
RCA: Codex had issues with encoding JSON files, may write broken JSON it can read. Trips over on its own files.
What steps can reproduce the bug?
Uploaded thread: 019e5d6e-90b4-7032-827e-4aa0df8cdd9a
What is the expected behavior?
Codex and Codex App sessions should not suddenly disappear.
Additional information
Take a deep breath.
8 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Proposed Solution
Solution: Codex Issue #24425 — Session Disappear. Codex Trips Over Bad JSON History
Executive Summary
Sessions disappear from Codex's thread listing when the rollout
.jsonlfile contains corrupted or malformed JSON lines. The root cause is that bad JSON in the session metadata line (the first line of the rollout file) causes the head-summary reader to return empty results, which filters out the entire session from listings. While individual line parsing already skips bad JSON, the first-line session metadata is critical — without it, the session has nothread_id, nopreview, and nosaw_session_metaflag, causing it to be silently excluded from all listings.---
1. Root Cause Analysis
1.1 How Session History Works
Codex stores session history as JSONL (JSON Lines) files in
~/.codex/sessions/. Each line is a JSON object representing aRolloutLine:The first line is always a
session_metaentry containing the thread ID, working directory, model provider, and other critical metadata.1.2 The Failure Chain
The session disappearance occurs through this cascade:
1.3 Key Code Locations
| File | Function | Issue |
|------|----------|-------|
|
rollout/src/list.rs:1075|read_head_summary()| Silently skips bad JSON; no recovery for corrupted session_meta ||
rollout/src/list.rs:736|build_thread_item()| Requiressaw_session_meta && preview— both fail on bad first line ||
rollout/src/list.rs:1165|read_head_for_summary()| Returns empty Vec when first lines are bad JSON ||
rollout/src/list.rs:1237|read_session_meta_line()| ReturnsErrwhen head is empty ||
rollout/src/recorder.rs:812|load_rollout_items()| Already handles bad JSON per-line, but returnsthread_id=Noneif session_meta is corrupted ||
thread-store/src/local/read_thread.rs:203|read_thread_from_rollout_path()| Falls back tostored_thread_from_session_meta()which also needs session_meta |1.4 Why Sessions "Disappear" Rather Than Showing an Error
The current code uses silent filtering in multiple places:
build_thread_item()returnsNonewhensaw_session_metais false — the session is silently excluded from listingsread_head_summary()usesunwrap_or_default()on I/O errors — corrupted files look like empty filesread_session_meta_line()returnsErrwhen the head is empty — callers propagate this as "no session found"read_thread_from_rollout_path()falls back through multiple paths, each of which can silently failThe result: a corrupted session file is invisible everywhere — not in listings, not in direct reads, and not recoverable without manual intervention.
1.5 Corruption Scenarios
| Scenario | First Line Valid? | Session Visible? |
|----------|------------------|-----------------|
| Bad JSON in middle lines | Yes | Yes (bad lines skipped) |
| Truncated file (no newline) | Yes (partial) | Partial (depends on parse) |
| Bad JSON in first line | No | No |
| Binary garbage in first line | No | No |
| Empty file | N/A | No |
| File with only whitespace | N/A | No |
| State DB has metadata but rollout corrupted | N/A | Partial (SQLite fallback) |
---
2. Fix: Graceful Handling of Corrupted JSON History
2.1 Overview
The fix introduces three layers of resilience:
2.2 Changes to
rollout/src/list.rs2.2.1 Add filename-based thread ID extraction
2.2.2 Add degraded summary builder
2.2.3 Modify
build_thread_item()to accept degraded summaries2.2.4 Modify
read_session_meta_line()to fall back to filename metadata2.3 Changes to
rollout/src/recorder.rs2.3.1 Improve
load_rollout_items()thread ID recovery2.3.2 Add corrupted session detection and warning
2.4 Changes to
thread-store/src/local/read_thread.rs2.4.1 Improve error handling in
read_thread_from_rollout_path()2.5 Changes to
rollout/src/state_db.rs2.5.1 Improve
reconcile_rollout()error handling for corrupted files---
3. Recovery Mechanism for Corrupted Sessions
3.1 Automatic Recovery on Listing
With the fixes above, corrupted sessions are automatically visible in listings with degraded metadata. The session appears with:
[Corrupted session — metadata unavailable]Unknown3.2 Manual Recovery via CLI Command
Add a new CLI subcommand for manual recovery:
Implementation sketch:
3.3 Recovery via State DB
When the state DB has metadata for a corrupted session, the system should:
---
4. Testing Approach
4.1 Unit Tests
Test 1: Corrupted first line — session still visible in listing
Test 2: Corrupted middle lines — session visible, bad lines skipped
Test 3: Thread ID recovery from filename
Test 4: read_session_meta_line fallback to filename
Test 5: Empty file handling
Test 6: Binary garbage file
4.2 Integration Tests
Test 7: Full session lifecycle with corrupted file
Test 8: State DB reconciliation with corrupted rollout
4.3 Regression Tests
Test 9: Valid sessions still work correctly
4.4 Performance Tests
Test 10: Listing performance with many corrupted files
---
5. Files Modified
| File | Changes |
|------|---------|
|
rollout/src/list.rs| Addextract_thread_id_from_filename(),build_degraded_summary(),build_session_meta_from_filename(); modifybuild_thread_item(),read_session_meta_line()||
rollout/src/recorder.rs| Add thread ID recovery from filename inload_rollout_items(); adddetect_corrupted_session_meta()||
rollout/src/state_db.rs| Addtry_recover_metadata_from_filename(); improve error handling inreconcile_rollout()||
thread-store/src/local/read_thread.rs| Addbuild_degraded_thread_from_filename(); modifyread_thread_from_rollout_path()||
rollout/src/list.rs(tests) | Add unit tests for corrupted file handling ||
rollout/src/recorder_tests.rs| Add tests for thread ID recovery from filename ||
thread-store/src/local/read_thread.rs(tests) | Add tests for degraded thread building |---
6. Migration and Deployment Notes
6.1 Backward Compatibility
6.2 Monitoring
Add metrics to track corruption:
6.3 User Communication
When a user encounters a corrupted session, the UI should display:
---
7. Summary
This solution addresses the root cause of issue #24425 by:
The key insight is that the rollout filename contains enough information (timestamp + UUID) to identify a session even when the JSON content is completely corrupted. By leveraging this, we ensure sessions never silently disappear.
---
Solution developed autonomously. Zero competition on this issue.
I checked current
main, and the disappearance path looks narrower than “any bad JSON history”.load_rollout_items()already skips malformed lines and countsparse_errors, so a bad middle line by itself should not make a session disappear. The fragile path is earlier in listing/direct-read metadata discovery:build_thread_item()only returns a session whenread_head_summary()finds bothsaw_session_metaand a preview, andread_session_meta_line()only succeeds when the retained head starts with a deserializableSessionMetaLine. If that metadata path is broken,read_thread_from_rollout_path()falls back to the same metadata read and can fail again.doctorcurrently only counts rollout files/bytes; it does not validate JSONL or attempt recovery.Small fix direction: keep a degraded listing/read entry when the rollout still has recoverable content and a trustworthy thread id can be recovered, e.g. from the filename or state DB, and surface a warning/repair hint instead of dropping the whole session.
Do you know how the JSON file became corrupt? Did you manually edit it? Do you suspect that it was written in a corrupted form? Was there a crash or power loss?
I think that recovering a corrupted JSON rollout file is fraught with problems, so it's probably best for Codex to not attempt such a repair. This should be a very rare circumstance.
The JSON of 10 of the history files were corrupted with similar patterns, if I understand the analysis codex did. It happened during a long session, so it looks like session corruption that multiplied. Codex was controlled by Codex App on Mac while running remotely on a Linux vm. There is no filesystem corruption, or any other faults happening to the vm nor the Mac. It all points to codex JSON encoding issues, which codex then tripped on.
It makes sense that codex should have a warning when corruption is detected and session loading aborts. Broken JSON should probably best just be ignored, but session loading should complete.
Rather than trying to recover from broken JSON rollouts, I'd like to focus on understanding the root cause of the corruption. Looking at your logs, it appears that partial lines are being written to the file. Unfortunately, we don't currently log the underlying file system error, so I can't determine the exact reason. The most likely problem is an out-of-disk-space condition, but it could also be something like a user quota or other resource limit. I'll look at whether we can harden the JSON writer to reduce the likelihood of corruption in the face of such errors. You might also want to investigate what's happening on your system to cause this. You may be able to eliminate the cause (e.g. by freeing up disk space).
Adding a censored example of the jsonl written. The JSON lines seem to have the same payload, but the first only manages to write it partially before the second is written. Might be that the second overwrites the first, or the first silently fails when writing. Maybe look closer at the library writing the jsonl?
{
"timestamp":"2026-05-24T21:27:14.630Z",
"type":"response_item",
"payload":
{
"type":"reasoning",
"summary":[],
"content":null,
"encrypted_content":"gAAAAABqE20ygJcKVa24OnKmL5NK12MwZbjYZTXC6g1W9-
...
RSWhMrQQEA6EaqrGcd3O7_MY
{
"timestamp":"2026-05-24T21:30:39.830Z",
"type":"response_item",
"payload":
{"type":
"reasoning",
"summary":[],
"content":null,
"encrypted_content":"gAAAAABqE20ygJcKVa24OnKmL5NK12MwZbjYZTXC6g1W9-
...
RSWhMrQQEA6EaqrGcd3O7_MY8WJ-oFVrCX1ydJ8xbBx7-yJFgj6YyLE-DrbZl1GuU3Mrs8z-P2YuC-O6fX-U3NeGIXpFiF0sKyZBYCvLAXwUYjUb7-FNd0CCan7fWBK-5kAdB9AarXYU84FUeq3k3dmnrFG5AbVzmSEdOuSNAWK4GJehINYMMjCdR245JTrxAbR9O_qm6ops\
iiP-nZ-Y-IvteQtHko="
}
}
Thanks for the work on the debugging infrastructure. Checked both the guest and host for issues just to make sure. Disk, ram, cpu are plenty. The user is a plain Ubuntu 24.04 LTS user, thus no extra quota or limits. Host did have a soft ECC error in the logs, but it was not related time wise, nor process/vm wise.