openai/codex · error · std::io::Error
invalid external agent session import ledger: {err}
Error message
invalid external agent session import ledger: {err} What it means
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.
Source
Thrown at codex-rs/external-agent-migration/src/sessions/ledger.rs:385
record.source_modified_at = Some(source_modified_at);
self.records.push(record);
Ok(true)
}
}
pub(crate) fn load_import_ledger(
codex_home: &Path,
) -> io::Result<ImportedExternalAgentSessionLedger> {
let path = import_ledger_path(codex_home);
let raw = match fs::read_to_string(path) {
Ok(raw) => raw,
Err(err) if err.kind() == io::ErrorKind::NotFound => {
return Ok(ImportedExternalAgentSessionLedger::default());
}
Err(err) => return Err(err),
};
serde_json::from_str(&raw).map_err(|err| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid external agent session import ledger: {err}"),
)
})
}
pub(crate) fn save_import_ledger(
codex_home: &Path,
ledger: &ImportedExternalAgentSessionLedger,
) -> io::Result<()> {
fs::create_dir_all(codex_home)?;
let path = import_ledger_path(codex_home);
let raw = serde_json::to_vec_pretty(ledger).map_err(io::Error::other)?;
fs::write(path, raw)
}
fn import_ledger_path(codex_home: &Path) -> PathBuf {
codex_home.join(SESSION_IMPORT_LEDGER_FILE)View on GitHub (pinned to 339751715c)
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
Example fix
// recovery: back up the ledger so it regenerates empty
let path = import_ledger_path(codex_home); // codex_home/<SESSION_IMPORT_LEDGER_FILE>
std::fs::rename(&path, path.with_extension("json.bak"))?;
// next load_import_ledger call returns a fresh default ledger Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: confirm the ledger parses before invoking import APIs
let path = codex_home.join(SESSION_IMPORT_LEDGER_FILE);
if let Ok(raw) = std::fs::read_to_string(&path) {
if serde_json::from_str::<serde_json::Value>(&raw).is_err() {
// quarantine the file now instead of failing later mid-import
}
} Try / catch
match load_import_ledger(codex_home) {
Err(err) if err.kind() == io::ErrorKind::InvalidData => {
// back up + remove the ledger, then retry once;
// accept that previously imported sessions may be re-offered
}
other => other,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- InvalidInput
- failed to parse app config for selected plugin `{plugin_id}`
- failed to parse MCP config for selected plugin `{plugin_id}`
- queued submission payload is invalid: {0}
- memory import requires at least one selected memory
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/6e15ccb818349e84.
Report an issue: GitHub.