Hmbown/CodeWhale · error · anyhow::Error

{error:#}; runtime preset rollback also failed: {}

Error message

{error:#}; runtime preset rollback also failed: {}

What it means

runtime_preset_error_with_rollback reports a double failure: applying a runtime preset failed AND the automatic rollback that restores each RuntimePresetFileSnapshot (prior file contents) also failed. The combined message joins the original apply error with every rollback failure, which means preset files may be left in a partially written state - neither the new preset nor the old contents.

Source

Thrown at crates/tui/src/tui/ui.rs:2946

                }
            },
        }
    }
}

fn runtime_preset_error_with_rollback(
    error: anyhow::Error,
    snapshots: &[&RuntimePresetFileSnapshot],
) -> anyhow::Error {
    let rollback_errors = snapshots
        .iter()
        .filter_map(|snapshot| snapshot.restore().err())
        .map(|error| format!("{error:#}"))
        .collect::<Vec<_>>();
    if rollback_errors.is_empty() {
        error
    } else {
        anyhow::anyhow!(
            "{error:#}; runtime preset rollback also failed: {}",
            rollback_errors.join("; ")
        )
    }
}

fn mark_active_turn_cancelled_locally(app: &mut App) {
    // #2739: every local cancel surface (Esc, Ctrl+C, approval abort, paused
    // command abort) must snapshot before it clears turn state. Otherwise
    // --continue reloads the previous save and the interrupted turn vanishes.
    app.streaming_state.reset();
    app.finalize_active_cell_as_interrupted();
    app.finalize_streaming_assistant_as_interrupted();
    persist_recovery_snapshot(app);
    app.is_loading = false;
    app.dispatch_started_at = None;
    app.turn_started_at = None;
    app.turn_last_activity_at = None;

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Read the message in two parts: the text before '; runtime preset rollback also failed' is the original apply error - fix that root cause (permissions, disk space, locks)
  2. Inspect every file named after 'rollback also failed' and reconcile its contents with your VCS or backup - do not assume the old values survived
  3. chmod u+w the preset files (or remount read-write), free disk space, pause sync clients, then re-apply the preset
  4. If the files are externally managed (dotfiles), remove them from the managed set or grant the TUI write access

Example fix

# before: preset file not writable
$ ls -l ~/.codewhale/runtime-presets/
-r--r--r-- 1 root root preset.json   # apply fails AND rollback fails

# after
$ sudo chown $USER ~/.codewhale/runtime-presets/* && chmod u+w ~/.codewhale/runtime-presets/*
# re-apply the preset
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust - verify preset snapshot targets are writable before applying
use std::os::unix::fs::PermissionsExt;
fn preset_paths_writable(paths: &[std::path::PathBuf]) -> bool {
    paths.iter().all(|p| match std::fs::metadata(p) {
        Ok(m) => m.permissions().mode() & 0o200 != 0,
        Err(_) => p.parent().map(|d| d.is_dir()).unwrap_or(false),
    })
}

Try / catch

match apply_runtime_preset(&preset, &snapshots) {
    Err(e) => {
        let combined = runtime_preset_error_with_rollback(e, &snapshots);
        alert_and_reconcile_files(&combined); // rollback may have failed: verify file contents against snapshots/VCS
    }
    Ok(()) => (),
}

Prevention

When it happens

Trigger: A preset apply writes config files and hits I/O errors (permission denied, read-only mount, disk full, locked files); snapshot.restore() then fails on the same files for the same underlying reason, so rollback_errors is non-empty and both errors are merged.

Common situations: Preset files in a read-only dotfiles checkout or owned by root; disk full mid-write; files locked by a sync client (Dropbox/iCloud) mid-apply; permissions changed between snapshot and restore.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/4ec3097f6e7cec90. Report an issue: GitHub.