{"record":{"id":"32321493100adb38","repo":"BigPizzaV3/CodexPlusPlus","slug":"empty-undo-token","errorCode":null,"errorMessage":"empty undo token","messagePattern":"empty undo token","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-data/src/storage.rs","lineNumber":809,"sourceCode":"        session_id: session_id.to_string(),\n        message,\n        undo_token: Some(token.to_string()),\n        backup_path: backup_path.map(|path| path.to_string_lossy().to_string()),\n    }\n}\n\nfn normalize_codex_thread_id(session_id: &str) -> String {\n    session_id\n        .strip_prefix(\"local:\")\n        .unwrap_or(session_id)\n        .to_string()\n}\n\nfn undo_backups(backup_store: &BackupStore, token: &str) -> anyhow::Result<Vec<Value>> {\n    let tokens =\n        serde_json::from_str::<Vec<String>>(token).unwrap_or_else(|_| vec![token.to_string()]);\n    if tokens.is_empty() {\n        anyhow::bail!(\"empty undo token\");\n    }\n    tokens\n        .into_iter()\n        .map(|token| backup_store.read_backup(&token))\n        .collect()\n}\n\nfn restore_backups(\n    backups: &[Value],\n    fallback_db_path: &Path,\n    allowed_db_paths: &[PathBuf],\n    codex_home: Option<&Path>,\n) -> anyhow::Result<()> {\n    for backup in backups {\n        let Some(tables) = backup[\"tables\"].as_object() else {\n            continue;\n        };\n        let source_db = backup_source_db(backup, fallback_db_path, allowed_db_paths)?;","sourceCodeStart":791,"sourceCodeEnd":827,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-data/src/storage.rs#L791-L827","documentation":"Thrown by undo_backups (reached through the undo path; also guards codex_thread_usage_history inputs) when the undo token parses to an empty token list — the caller supplied an effectively blank token. The offending input is the undo_token string; without at least one token there is no backup to restore, so the guard fires before opening the backup store.","triggerScenarios":"Thrown at crates/codex-plus-data/src/storage.rs:809 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["使用 delete_local 返回结果中的原始 undo_token，不要手工构造","确认 token 字符串没有被截断或清空","撤销前确认对应备份仍存在且未被消费","对空 token 提前在前端拦截并提示"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}