| name | codex-desktop-session-repair |
| description | Use this when Codex Desktop conversations are missing from the sidebar, have broken or mixed model_provider IDs, appear under the wrong project, or need careful repair across ~/.codex session JSONL files and Codex Desktop state SQLite databases. |
Codex Desktop Session Repair
Use this skill for forensic repair of Codex Desktop local conversation data. Do not treat the data as a single flat set of JSONL files: recent Desktop builds keep sidebar state in ~/.codex/state_*.sqlite, while the full transcript still lives under ~/.codex/sessions/**/*.jsonl.
Prefer careful inspection and small, reversible edits over one-shot normalization. Provider IDs, database schema versions, and sidebar grouping rules can change between builds.
Core Rules
- Back up every file before editing it.
- Parse JSONL with a JSON parser. Do not use ad hoc string replacement for records.
- Prefer the newest or actively opened
~/.codex/state_*.sqlite database. If Codex Desktop is running, lsof -p <app-server-pid> can show which state DB it has open.
- Update both the JSONL source record and the SQLite
threads row when repairing metadata used by the sidebar.
- Do not rewrite historical
turn_context or exec_command events unless the user explicitly asks. Those record what actually happened at the time.
- Close and reopen Codex Desktop after metadata repairs so the sidebar reloads state.
Identify Storage
Find the active Desktop app-server process and state database:
ps auxww | rg -i 'codex.*app-server|Codex'
lsof -nP -p <PID> | rg 'state_[0-9]+\.sqlite|sessions/.+jsonl'
Inspect schema before assuming column names:
sqlite3 ~/.codex/state_5.sqlite '.tables'
sqlite3 ~/.codex/state_5.sqlite '.schema threads'
Typical thread metadata lives in threads:
id
title
rollout_path
created_at, updated_at, created_at_ms, updated_at_ms
source
model_provider
cwd
archived
Audit Sidebar Counts
Compare visible, non-archived Desktop conversations against session files:
sqlite3 -header -column ~/.codex/state_5.sqlite \
"SELECT id,title,cwd,source,archived,datetime(updated_at,'unixepoch') AS updated
FROM threads
WHERE archived=0
ORDER BY updated_at_ms DESC;"
If old session_index.jsonl exists, treat it as auxiliary. It may not be the current sidebar source:
wc -l ~/.codex/session_index.jsonl
rg '"type":"session_meta"' ~/.codex/sessions -g '*.jsonl'
When comparing counts, separate user-visible sessions from subagent or guardian sessions. In JSONL, internal sessions often have payload.source as an object rather than a simple string.
Repair Model Provider IDs
First determine the expected provider ID from a healthy fresh conversation or the current Desktop database. For OpenAI-backed Desktop sessions it is commonly:
openai
Do not map provider IDs to short aliases such as ai unless that exact provider exists in the active Codex configuration. A broken SQLite cache can make a thread visible in the sidebar but fail when opened with an error like:
failed to load configuration: Model provider `ai` not found
Audit before changing anything:
sqlite3 -header -column ~/.codex/state_5.sqlite \
"SELECT model_provider,count(*) FROM threads GROUP BY model_provider;"
For JSONL, inspect only the first session_meta record. If repairing a session, update:
- first JSONL record:
payload.model_provider
- SQLite row:
threads.model_provider
If the active configuration and healthy fresh threads use openai, normalize stale rows in the active state DB too:
sqlite3 ~/.codex/state_5.sqlite ".backup ~/.codex/state_5.sqlite.providerfix.bak"
sqlite3 ~/.codex/state_5.sqlite \
"BEGIN IMMEDIATE;
UPDATE threads SET model_provider='openai'
WHERE model_provider <> 'openai';
COMMIT;"
sqlite3 ~/.codex/state_5.sqlite \
"SELECT model_provider,count(*) FROM threads GROUP BY model_provider;"
Keep a per-file backup and verify every edited JSONL file still parses.
Repair Missing Project Conversations
If a conversation exists in the database but is absent from the expected project group in the sidebar, compare:
threads.cwd
- first JSONL
session_meta.payload.cwd
- paths referenced in user messages, tool calls, and final answers
- nearby healthy conversations for that project
- whether the candidate project directory still exists locally
Common failure mode:
threads.cwd = /Users/mac/Codes
actual project = /Users/mac/Codes/lore
This can hide the thread from Project > lore while also making it awkward in the global conversation list.
Only repair cwd when evidence is strong. Good evidence includes repeated references to the child project path, final artifacts inside that project, or user confirmation.
Project vs Conversation Classification
Do not force every broad or stale cwd into a project. Some conversations belong in the generic Conversation list.
Keep a thread as a generic conversation when:
- It is a Computer Use, browser-control, media-playback, app-install, OS operation, or casual chat task.
- Its recorded
cwd is a broad parent such as /Users/mac/Codes, but there is no strong repeated child-project evidence.
- Its recorded
cwd or candidate project directory no longer exists locally.
- Mentions of a project path are incidental context rather than the workspace being worked on.
Repair the thread into a project when:
- The current
cwd is a broad parent or temporary Codex directory.
- The transcript strongly indicates a specific child project, for example
/Users/mac/Codes/lore.
- That child directory still exists locally.
- The target project matches nearby healthy Desktop threads or user confirmation.
In short: broad Computer Use and stale local directories stay under conversations; existing, evidence-backed project directories get repaired like the 清理 lore 仓库后重克隆 case.
Repair both sources:
- Back up the rollout JSONL.
- Back up the SQLite DB with
.backup.
- Change only the first
session_meta.payload.cwd.
- Update
threads.cwd for the same id.
- Leave historical
turn_context.cwd and tool-event cwd records unchanged.
Example SQLite update:
sqlite3 ~/.codex/state_5.sqlite \
"UPDATE threads
SET cwd='/Users/mac/Codes/lore'
WHERE id='THREAD_ID'
AND cwd='/Users/mac/Codes';"
Verify Repairs
Always run:
sqlite3 ~/.codex/state_5.sqlite 'PRAGMA integrity_check;'
Verify the target thread:
sqlite3 -header -column ~/.codex/state_5.sqlite \
"SELECT id,title,cwd,model_provider,archived,datetime(updated_at,'unixepoch') AS updated
FROM threads
WHERE id='THREAD_ID';"
Verify JSONL parsing with a real parser:
node - <<'NODE'
const fs = require('fs');
const file = process.argv[1];
let n = 0;
for (const line of fs.readFileSync(file, 'utf8').split(/\n/)) {
if (!line) continue;
JSON.parse(line);
n++;
}
console.log(`jsonl_records=${n}`);
NODE /path/to/rollout.jsonl
Ask the user to restart Codex Desktop and confirm the sidebar result.
Restore From Backup
For JSONL, restore the backup file over the edited rollout.
For SQLite, close Codex Desktop first, then restore the .backup copy:
cp ~/.codex/state_5.sqlite.before-repair ~/.codex/state_5.sqlite
If WAL or SHM files are present and Desktop is closed, remove only the matching restored database sidecars when necessary:
rm -f ~/.codex/state_5.sqlite-wal ~/.codex/state_5.sqlite-shm