Install
openclaw skills install @wardlu/codex-cross-provider-session-repairRepair Codex Desktop sessions that fail after switching model providers or importing/forking old conversations. Use this skill whenever a user mentions an old Codex session becoming invalid, “model provider not found”, “Codex cannot load config.toml”, remote context compaction 404s, “Item with id rs_... not found”, repeated reconnecting during resume, or a migrated Codex conversation that cannot continue—even when the user only asks to inspect or explain the failure. Diagnose the target session across its JSONL rollout, root state_5.sqlite, config.toml, and logs_2.sqlite, then make a target-scoped backup-first repair with verification and restart instructions.
openclaw skills install @wardlu/codex-cross-provider-session-repairCompatibility: Requires Python 3.9+ with the standard library; Windows, macOS, and Linux Codex homes are supported. Stop Codex Desktop before applying file or database changes. On macOS, the recommended launcher requires Terminal automation permission so it can close only its uniquely titled repair window.
Use this skill for a damaged or incompatible saved Codex session, not for ordinary model/API troubleshooting. The goal is to make one existing conversation resumable while keeping its user-visible history and avoiding broad cache deletion.
The user does not need to memorise CLI flags. A typical session looks like this:
scripts/start_repair.py immediately. On macOS it opens a readable Terminal tab/window and runs the independent worker through a short temporary runner; do not paste the full worker command into the user's shell. The worker is safe whether Codex is still open, is being closed, or was already closed after the approval. Immediately tell the user in the current conversation: 修复任务已启动,最多等待 300 秒;请完全退出 Codex。终端会持续显示进度,完成前不要重新打开。 / Repair started; it may wait up to 300 seconds. Fully quit Codex; follow the Terminal status and do not reopen it before completion.Verified / 已验证. The Terminal shows bilingual states: Waiting / 等待中, Applying / 修复中, Verified / 已验证, or Failed / 失败. During the default 300-second wait it prints reminders at about 60, 180, and 240 seconds. The status JSON also exposes wait_timeout_seconds, wait_phase, conversation_notice, and can_reopen; only can_reopen: true permits reopening. If the current conversation remains available, mirror the wait/reminder status there; after Codex is fully closed, the independent Terminal and status JSON are authoritative.If the user does not know the UUID, help them find it:
From the Codex App UI: right-click the broken conversation in the sidebar; some versions show the ID in the context menu or in the URL bar.
From the filesystem: list recent rollout files:
ls -lt ~/.codex/sessions/*/*/rollout-*.jsonl | head -10
The UUID is the last segment of the filename (e.g. rollout-2026-08-02T22-05-32-<UUID>.jsonl).
From the database: query the threads table for recent sessions:
import sqlite3
conn = sqlite3.connect("~/.codex/state_5.sqlite".replace("~", str(__import__("pathlib").Path.home())))
for row in conn.execute("SELECT id, title, model, updated_at FROM threads ORDER BY updated_at DESC LIMIT 10"):
print(row)
After Codex Desktop is fully quit, the manual one-shot command can combine all
needed flags in a single --apply invocation:
python3 <skill_dir>/scripts/repair.py --session-id <UUID> --codex-home <CODEX_HOME> --fix-provider --provider <current> --fix-model-turn --remove-reasoning all --apply
Run the same command without --apply afterward. Do not report completion until
the post-repair report has been captured and verified.
Codex stores continuation state in several layers:
config.toml (current provider defaults);sessions/**/rollout-*.jsonl file (the conversation event stream);state_5.sqlite threads row (the desktop index and provider snapshot); andlogs_2.sqlite (diagnostic evidence).After a provider switch, an old thread can retain a provider in layers 2 or 3 even when the global config is correct. Separately, an imported/forked long thread can contain response_item reasoning records that were sent with store=false and never persisted by the service. Remote compaction then fails with HTTP 404: Item with id 'rs_...' not found.
The second failure is not fixed by changing the provider, deleting config.toml, or clearing browser cache. If multiple different rs_... IDs fail in succession, remove the non-user-visible local reasoning items in one pass; deleting one ID at a time only exposes the next stale reference.
A third failure occurs when switching to a Gemini-backed model (e.g. gemini-3.6-flash-high via a proxy such as CC Switch). Gemini's API rejects requests whose message history ends with a model/assistant turn: Requests ending with a model turn are not supported. (HTTP 400 INVALID_ARGUMENT). Codex triggers pre-sampling context compaction (CompHashChanged) on model switch; if the effective history after thread_rolled_back events ends with an assistant message, the compaction request fails and the turn cannot start. This is systemic—every session switched to Gemini that needs compaction will hit it until the JSONL is repaired.
A fourth failure occurs when the current provider/model does not implement
Codex's remote compaction v2. The turn fails with Error running remote compact task: Fatal error: remote compaction v2 expected exactly one compaction output item, got 0 from N output items. This is not a JSONL corruption: the backend
simply did not return the type: "compaction" output item Codex requires. The
report prints a user-friendly hint and the operator should either disable
remote compaction or switch to a provider/model that supports it. The script
offers --disable-remote-compaction (requires --apply and explicit user
approval) to write remote_compaction_v2 = false under [features] in
config.toml with a backup; after applying, fully quit and relaunch Codex
Desktop.
A fifth failure occurs when an imported session is pinned to a model that the
current ChatGPT account cannot use, such as ark-code-latest. The report
identifies the model in the rollout's structured turn settings and the target
threads.model snapshot. Use --fix-model --model <current-model> to update
only those target-session values. Pair it with --remove-reasoning none when
the session has no stale-reasoning diagnosis; this preserves all local
reasoning records.
codex app-server/service processes, before applying changes. A live process can cache old events or hold a file lock.verified.config.toml, auth tokens, caches, or databases as a generic “reset”.event_msg. Only remove response_item records whose payload type is reasoning, and only when the diagnosis supports the stale-compaction repair.Use the exact session UUID supplied by the user. Resolve CODEX_HOME first; on Windows fall back to %USERPROFILE%\\.codex, and on macOS/Linux to $HOME/.codex. Do not repurpose common environment variables in shell snippets.
Locate the rollout by filename suffix and then confirm its first session_meta record contains the requested ID. Do not trust a title or current working directory alone; imported files can contain more than one session_meta record.
Run the bundled script in dry-run mode:
python scripts/repair.py --session-id <UUID> --codex-home <CODEX_HOME>
The report should include:
session_meta provider values;config.toml;threads.model_provider from root state_5.sqlite (the important database, when present);rs_... IDs extracted from target-thread log errors; andresponse_item reasoning records.Use Python's built-in sqlite3 module; do not require the optional sqlite3 CLI. Treat a missing sqlite/ child database as normal and do not invent one.
Provider mismatch. If the current provider is known and the target session or target DB row still names an old provider, apply a target-only provider repair:
python scripts/repair.py --session-id <UUID> --codex-home <CODEX_HOME> \
--provider <current-provider> --fix-provider --apply
Update only the target session's session_meta payload and the target threads row. Never run a global UPDATE ... WHERE model_provider=... for a shared Codex home.
Unsupported saved model. If the report shows a model such as
ark-code-latest that the current account does not support, align only the
target rollout settings and root thread snapshot with the current configured
model:
python scripts/repair.py --session-id <UUID> --codex-home <CODEX_HOME> \
--model <current-model> --fix-model --remove-reasoning none --apply
This creates a JSONL backup and a database backup when the target row changes.
It does not delete reasoning records or alter the global config.toml.
Stale remote compaction. If logs show Item with id 'rs_...' not found and the IDs map to local response_item records with payload.type == "reasoning", use:
python scripts/repair.py --session-id <UUID> --codex-home <CODEX_HOME> \
--remove-reasoning stale --apply
This removes only the exact stale IDs. If the user has already seen two or more different stale IDs from the same imported/migrated session, or a retry reveals another ID immediately, use the one-pass cleanup:
python scripts/repair.py --session-id <UUID> --codex-home <CODEX_HOME> \
--remove-reasoning all --apply
all means all local response_item records whose payload type is reasoning; it does not remove event_msg.agent_reasoning, visible messages, tool calls, or tool outputs. This is appropriate because those reasoning items are internal model traces and the service has already confirmed that they were not persisted.
Provider repair and reasoning cleanup can be combined in one invocation after the dry-run report confirms both conditions.
For the recommended user-facing execution, start this command immediately after the user approves the repair. On macOS it opens a visible Terminal window, shows the final result, and closes its uniquely titled repair window after the user presses Enter:
python3 <skill_dir>/scripts/start_repair.py --session-id <UUID> \
--codex-home <CODEX_HOME> --remove-reasoning stale
The command passes --allow-already-stopped to the worker, so the user may
quit Codex before or after the worker starts. The worker still requires a
stable process-free window and a rollout fingerprint match before writing.
Terminal output and the status file use bilingual labels. The Terminal opens a
120-column by 36-row window, hides the long worker command behind a temporary
runner, and first announces the maximum wait, then reminds the user at about
60, 180, and 240 seconds. Verified / 已验证 means it is safe to reopen; Failed / 失败
means keep Codex closed. The launcher tags the repair window with the session
ID; after the final Enter prompt, it closes that exact window when it contains
only the repair tab and exits its shell. If macOS has not granted Terminal
automation permission, the worker still exits normally and the user can close
the finished window with Command+W. A timeout writes wait_timed_out: true
and a conversation-ready next-step message without changing the session.
For advanced use, start the lower-level process-aware worker from a Terminal that is independent of Codex while Codex is still open:
python3 <skill_dir>/scripts/wait_and_repair.py --session-id <UUID> --codex-home <CODEX_HOME> --remove-reasoning all --status-file /tmp/codex-session-repair-<UUID>.json
Then fully quit Codex Desktop. The wrapper prints bilingual status labels and
atomically updates the same status file. It announces the configured wait
limit and emits periodic reminders. If Codex reappears during the stability
window or the rollout changes while waiting, it stops without writing and
reports Failed / 失败.
Gemini model-turn compaction. If the dry-run report shows Model-turn compaction: RISK or logged-error, the effective history ends with an assistant message and Gemini compaction will fail. Use:
python scripts/repair.py --session-id <UUID> --codex-home <CODEX_HOME> \
--fix-model-turn --apply
This removes thread_rolled_back event records so that previously rolled-back turns (which typically contain the trailing user message) become effective again. If the last effective message is still assistant after rollback removal, a dummy user message is appended to satisfy Gemini's requirement. The repair can be combined with --fix-provider and --remove-reasoning in the same invocation.
Run the script again without --apply, or use its --verify output. Confirm:
threads.model and all structured rollout model settings agree with the chosen model when model repair was requested;Model-turn compaction line shows ok (not RISK) and last_role=user when --fix-model-turn was applied.The report labels log-derived IDs as Historical remote stale IDs (from logs).
That list may remain after a successful repair because it is historical evidence.
The decisive fields are Stale IDs present as local reasoning: [] together with
Local reasoning records: 0. The raw rs_... text may still occur inside a
historical task_complete error event; that text is not a response_item
submitted for compaction and does not need to be deleted.
Tell the user to fully quit and relaunch Codex Desktop, then open the exact session. A page refresh is insufficient because the desktop process caches the event stream. A safe smoke test is a short prompt that asks for a confirmation only; do not resume the user's old project task automatically.
If compaction succeeds and the error changes to authentication, transport, or provider availability, stop treating it as a session-file problem and diagnose that separate issue. If a new stale ID appears after a clean reload, re-run the dry-run report before changing anything else.
Install this skill with npx skills add WardLu/skills --skill codex-cross-provider-session-repair --global --agent codex --yes, or use the platform installer documented in README.md.
scripts/repair.py — deterministic, backup-first diagnosis and target-scoped repair.scripts/start_repair.py — recommended consent-to-terminal launcher; opens a readable 120×36 Terminal tab/window on macOS, hides the long worker command behind a temporary runner, and starts the worker even when Codex was already closed.scripts/wait_and_repair.py — process-aware wait/apply/verify wrapper for use from an independent Terminal. It fails closed if Codex was not detected initially, reappears during the stability window, or changes the rollout while waiting.tests/test_repair.py — offline tests using temporary fake Codex homes and SQLite databases.tests/test_wait_and_repair.py — offline tests for process lifecycle, rollout race protection, status files, and end-to-end verification.README.md — installation, upgrade, release, and troubleshooting guide.The English installation and maintenance guide is README.md; Simplified Chinese users can use the synchronized README.zh-CN.md guide.
Give the user a concise result with: root cause, exact target session, files changed, backup paths, verification counts, whether the independent process-aware run reached verified, whether an independent smoke test reached Context compacted, and the one required restart step. When the wait ends in a timeout, explicitly state that the configured wait limit was reached, no files were changed, and the user must fully quit Codex before retrying. Never include credentials or full JSONL lines.