BigPizzaV3/CodexPlusPlus · error · anyhow::Error
empty undo token
Error message
empty undo token
What it means
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.
Source
Thrown at crates/codex-plus-data/src/storage.rs:809
session_id: session_id.to_string(),
message,
undo_token: Some(token.to_string()),
backup_path: backup_path.map(|path| path.to_string_lossy().to_string()),
}
}
fn normalize_codex_thread_id(session_id: &str) -> String {
session_id
.strip_prefix("local:")
.unwrap_or(session_id)
.to_string()
}
fn undo_backups(backup_store: &BackupStore, token: &str) -> anyhow::Result<Vec<Value>> {
let tokens =
serde_json::from_str::<Vec<String>>(token).unwrap_or_else(|_| vec![token.to_string()]);
if tokens.is_empty() {
anyhow::bail!("empty undo token");
}
tokens
.into_iter()
.map(|token| backup_store.read_backup(&token))
.collect()
}
fn restore_backups(
backups: &[Value],
fallback_db_path: &Path,
allowed_db_paths: &[PathBuf],
codex_home: Option<&Path>,
) -> anyhow::Result<()> {
for backup in backups {
let Some(tables) = backup["tables"].as_object() else {
continue;
};
let source_db = backup_source_db(backup, fallback_db_path, allowed_db_paths)?;View on GitHub (pinned to f2074595a2)
Solutions
- 使用 delete_local 返回结果中的原始 undo_token,不要手工构造
- 确认 token 字符串没有被截断或清空
- 撤销前确认对应备份仍存在且未被消费
- 对空 token 提前在前端拦截并提示
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at crates/codex-plus-data/src/storage.rs:809 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@f2074595a2 (2026-08-23).
Data as JSON: /api/errors/32321493100adb38.
Report an issue: GitHub.