Hmbown/CodeWhale · critical

; ROLLBACK FAILED — the pre-import document is preserved at

Error message

{error:#}; ROLLBACK FAILED — the pre-import document is preserved at {}

What it means

The most dangerous import outcome: the apply step failed AND the rollback also failed (rollback_import_target or store.reload() errored). The library raises the original error plus "ROLLBACK FAILED" and points at the backup file path where the pre-import document was preserved, so the user can restore manually.

Solutions

  1. Manually restore the pre-import document from the backup path printed in the error message.
  2. Check disk space and file permissions on the config location, fix them, and retry the import.
  3. Ensure no other process is holding the config file or the backup open/locked.
  4. After restoring, diff the live config against the backup to confirm state, then re-run the corrected import.

Example fix

// recovery
cp ~/.config/codewhale/config.toml.bak ~/.config/codewhale/config.toml
# then retry the import after fixing disk/permissions
Defensive patterns

Strategy: fallback

Validate before calling

// Before importing, verify the config dir is writable and has free space:
// df -h ~/.config/codewhale && test -w ~/.config/codewhale/config.toml && echo ok

Try / catch

match run_import(&bundle, &store, scope) {
    Err(e) if e.to_string().contains("ROLLBACK FAILED") => {
        let backup = extract_backup_path(&e.to_string());
        eprintln!("restore manually: cp {backup:?} <config path>, then diff before retrying");
    }
    other => other?,
}

Prevention

When it happens

Trigger: apply(candidate, ...) errors during apply_bundle/run_import, then rollback_import_target (restoring the backup or deleting a config created during the transaction) or store.reload() also fails — e.g. the backup path is gone, the disk is full, or the config file is locked/read-only.

Common situations: Disk-full or permission problems preventing the backup restore; another process deleting the backup mid-import; the target config file made read-only by another tool; a config created during the transaction that could not be removed.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/37c948af254e09b9. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/config_bundles.rs:1091

    let original_config = store.config.clone();
    let backup_path = if target
        .try_exists()
        .with_context(|| format!("checking config target {}", target.display()))?
    {
        Some(create_collision_safe_backup(&target)?)
    } else {
        None
    };

    let mut target_written = false;
    let apply_result = apply(candidate, store, &mut target_written);
    if let Err(error) = apply_result {
        store.config = original_config;
        let rollback = rollback_import_target(&target, backup_path.as_deref(), target_written)
            .and_then(|()| store.reload());
        match rollback {
            Ok(()) => bail!("{error:#}; rolled back to the pre-import document"),
            Err(_) => bail!(
                "{error:#}; ROLLBACK FAILED — the pre-import document is preserved at {}",
                backup_path
                    .as_deref()
                    .map(Path::display)
                    .map(|path| path.to_string())
                    .unwrap_or_else(
                        || "<no prior file; remove the new target manually>".to_string()
                    )
            ),
        }
    }

    Ok(ImportReceipt {
        plan,
        backup_path,
        target,
    })
}

View on GitHub (pinned to 73e0f67d83)