{"record":{"id":"6e15ccb818349e84","repo":"openai/codex","slug":"invalid-external-agent-session-import-ledger-err","errorCode":null,"errorMessage":"invalid external agent session import ledger: {err}","messagePattern":"invalid external agent session import ledger: (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"codex-rs/external-agent-migration/src/sessions/ledger.rs","lineNumber":385,"sourceCode":"        record.source_modified_at = Some(source_modified_at);\n        self.records.push(record);\n        Ok(true)\n    }\n}\n\npub(crate) fn load_import_ledger(\n    codex_home: &Path,\n) -> io::Result<ImportedExternalAgentSessionLedger> {\n    let path = import_ledger_path(codex_home);\n    let raw = match fs::read_to_string(path) {\n        Ok(raw) => raw,\n        Err(err) if err.kind() == io::ErrorKind::NotFound => {\n            return Ok(ImportedExternalAgentSessionLedger::default());\n        }\n        Err(err) => return Err(err),\n    };\n    serde_json::from_str(&raw).map_err(|err| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"invalid external agent session import ledger: {err}\"),\n        )\n    })\n}\n\npub(crate) fn save_import_ledger(\n    codex_home: &Path,\n    ledger: &ImportedExternalAgentSessionLedger,\n) -> io::Result<()> {\n    fs::create_dir_all(codex_home)?;\n    let path = import_ledger_path(codex_home);\n    let raw = serde_json::to_vec_pretty(ledger).map_err(io::Error::other)?;\n    fs::write(path, raw)\n}\n\nfn import_ledger_path(codex_home: &Path) -> PathBuf {\n    codex_home.join(SESSION_IMPORT_LEDGER_FILE)","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/external-agent-migration/src/sessions/ledger.rs#L367-L403","documentation":"load_import_ledger (codex-rs/external-agent-migration/src/sessions/ledger.rs:373-390) reads the session-import ledger JSON under codex_home. A missing file is normal (an empty default ledger is returned), but content that fails serde parsing becomes io::Error(InvalidData) with this message. Because every session-import API - detect_recent_sessions, has_current_session_been_imported, find_existing_session_import, checkpoint_existing_session_import, record_completed_session_imports, record_detected_session_connectors - loads the ledger, one unreadable file blocks the entire import feature.","triggerScenarios":"The ledger file exists but is not valid JSON for the ledger schema: truncated by a crash or disk-full during save_import_ledger's plain fs::write (non-atomic), hand-edited, or written in an incompatible format by a different codex version.","commonSituations":"Process killed or power lost mid-import; user or a sync tool edited the file; downgrading codex after a newer version changed ledger fields; a full disk leaving a partial write.","solutions":["Back up and delete the ledger file under codex_home - it regenerates as an empty ledger; the trade-off is prior import records are forgotten, so already-imported sessions may be offered again","Alternatively repair the JSON to satisfy the ledger struct if the records matter (truncation usually just drops the closing bracket)","Fix the root cause (free disk space; avoid killing the process mid-import) before re-importing so the rewrite cannot corrupt again"],"exampleFix":"// recovery: back up the ledger so it regenerates empty\nlet path = import_ledger_path(codex_home); // codex_home/<SESSION_IMPORT_LEDGER_FILE>\nstd::fs::rename(&path, path.with_extension(\"json.bak\"))?;\n// next load_import_ledger call returns a fresh default ledger","handlingStrategy":"try-catch","validationCode":"// pre-flight: confirm the ledger parses before invoking import APIs\nlet path = codex_home.join(SESSION_IMPORT_LEDGER_FILE);\nif let Ok(raw) = std::fs::read_to_string(&path) {\n    if serde_json::from_str::<serde_json::Value>(&raw).is_err() {\n        // quarantine the file now instead of failing later mid-import\n    }\n}","typeGuard":null,"tryCatchPattern":"match load_import_ledger(codex_home) {\n    Err(err) if err.kind() == io::ErrorKind::InvalidData => {\n        // back up + remove the ledger, then retry once;\n        // accept that previously imported sessions may be re-offered\n    }\n    other => other,\n}","preventionTips":["ErrorKind::InvalidData with this message always means the on-disk JSON, not your arguments","Keep the ledger file out of editors and sync tools","Snapshot the ledger before large imports if crashes are common in your flow","After recovery, expect detect_recent_sessions to re-offer previously imported sessions"],"tags":["migration","session-import","json","corruption","rust"],"backgroundTag":"corrupt-json-file","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}