Hmbown/CodeWhale · error

; rolled back to the pre-import document

Error message

{error:#}; rolled back to the pre-import document

What it means

During apply_prepared_bundle, if the apply callback fails, the library rolls back: it restores the in-memory original config and attempts rollback_import_target plus store.reload(). If rollback succeeds, the original apply error is re-raised with the suffix "; rolled back to the pre-import document", telling the user the import failed atomically and nothing was changed.

Solutions

  1. Read the leading part of the message ({error:#}) — that is the real cause; fix it in the bundle.
  2. Retry the import after correcting the bundle; the pre-import document is intact so no manual restore is needed.
  3. Dry-run the import first to catch plan-level problems before apply.
  4. If the cause is a concurrent edit, re-read the current config and re-attempt when no other process is editing.

Example fix

// before
apply = writes invalid provider slot -> Err -> rolled back
// after
dry-run import first, fix the offending bundle entry, then apply cleanly
Defensive patterns

Strategy: try-catch

Try / catch

match run_import(&bundle, &store, scope) {
    Err(e) if e.to_string().ends_with("rolled back to the pre-import document") => {
        // safe: nothing changed; fix the root cause (prefix of the message) and retry
        eprintln!("import failed cleanly: {e:#}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: The user-supplied apply callback (or CAS-guarded store write) returns Err during run_import/apply_bundle; rollback of the target file and store reload both succeed, so this compound error is raised instead of the bare apply error.

Common situations: Importing a bundle whose values fail downstream validation in the apply step (e.g. invalid provider slot overrides); a CAS conflict with a concurrent editor that surfaced mid-apply; a migration callback erroring during route import.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

    let target = store.path().to_path_buf();
    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)