BigPizzaV3/CodexPlusPlus · error
rollout changed before rollback
Error message
rollout changed before rollback: {} What it means
restore_bulk_session_rewrite refuses to overwrite a rollout file whose current SHA-256 no longer matches the `rewritten_sha256` recorded when the bulk rewrite was applied. This safety check prevents clobbering newer content with the stored pre-image, so the file is reported as 'changed before rollback'.
Solutions
- Stop the Codex client (or anything writing that rollout file) so files stop changing, then redo the rollback
- Restore that specific file from an external backup — the library intentionally will not overwrite diverged files
- If the current content is actually wanted, remove it from the pending changes list instead of forcing a restore
- Serialize undo/sync operations with a lock to prevent concurrent rewrites
Example fix
// before: running undo while codex is live -> file hash drifts after rewrite $ codex-plus undo # rollback of rollout.jsonl fails: changed before rollback // after: stop codex first, then undo $ codex quit && codex-plus undo
Defensive patterns
Strategy: validation
Validate before calling
let current = sha256_file(&change.plan.path)?;
if current != change.rewritten_sha256 {
// file drifted; restore from external backup instead of library rollback
} Try / catch
match restore_bulk_session_rewrite(change) {
Err(e) if e.to_string().contains("changed before rollback") => {
eprintln!("file drifted; use external backup for {}", change.plan.path.display());
}
other => other?,
} Prevention
- Never edit rollout files between apply and rollback
- Stop the Codex client so it stops appending to session rollouts
- Use a lock to serialize undo/sync against other operations
When it happens
Trigger: Between the bulk session rewrite and the rollback, the rollout file at change.plan.path was modified — by the Codex client appending events, by another sync run, or by manual editing — so sha256_file(path) != change.rewritten_sha256.
Common situations: Codex client still running and appending to session rollouts during restore; two sync/undo operations racing on the same session; user manually editing rollout JSON before undoing.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- DreamSkin 主题包 SHA-256 与审核元数据不一致
- 微信连接运行锁已损坏
- SHA-256 校验失败
- Remote Control recovery state lock poisoned
- archived lookup task failed
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/bba8c3b6bafc1551.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-data/src/provider_sync.rs:3668
let mut restore_errors = Vec::new();
for change in changes.iter().rev() {
if let Err(error) = restore_bulk_session_rewrite(change) {
restore_errors.push(format!("{}: {error:#}", change.plan.path.display()));
}
}
if !restore_errors.is_empty() {
return Err(anyhow::anyhow!(
"failed to restore {} rollout file(s): {}",
restore_errors.len(),
restore_errors.join("; ")
));
}
Ok(())
}
fn restore_bulk_session_rewrite(change: &AppliedBulkSessionRewrite) -> anyhow::Result<()> {
if sha256_file(&change.plan.path)? != change.rewritten_sha256 {
return Err(anyhow::anyhow!(
"rollout changed before rollback: {}",
change.plan.path.display()
));
}
codex_plus_core::settings::atomic_write_with(&change.plan.path, |file| {
let mut writer = HashingWriter::new(BufWriter::new(file));
let source_sha256 = stream_restore_rollout_session_meta_lines(
&change.plan.path,
&change.plan.original_session_meta_lines,
&mut writer,
)?;
writer.flush()?;
if source_sha256 != change.rewritten_sha256
|| sha256_file(&change.plan.path)? != change.rewritten_sha256
{
return Err(std::io::Error::other(BULK_SESSION_SOURCE_CHANGED_ERROR));
}View on GitHub (pinned to b1ed92e5e4)