ramensoftware/windhawk · warning

Failed

Error message

{pos} Failed {id}: {reason}

What it means

A per-mod failure line logged during `data import` progress: '<pos> Failed <mod_id>: <reason>'. When the core reports ImportProgressStatus::Failed for a mod, the CLI logs the reason (or a generic 'import failed') as a warning and continues with the remaining mods.

Solutions

  1. Read the <reason> after the mod id to learn the specific failure
  2. Retry the import for the failed mod alone after fixing its cause
  3. Check the final exit summary — partial imports still report overall status
  4. Manually install the failing mod from its repository/local source

Example fix

// before
windhawk data import backup.whc --yes   # exits 0, mod X failed silently in logs
// after
windhawk data import backup.whc --yes || true
grep 'Failed ' import.log   # find failed mod ids and re-import them individually
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check: ensure the archive's mod versions still exist in the repository before importing

Try / catch

match import(env, args) {
    Ok(summary) => for line in &summary.failures { eprintln!("retry mod: {line}") },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Importing an archive where a specific mod fails to install — e.g. its source no longer compiles, its version is unavailable, or its settings cannot be applied — while the overall import proceeds.

Common situations: Restoring an old backup onto a newer Windhawk where a mod version is gone; mods with local source that was never archived; compilation failures during mod installation.

Related errors


AI-assisted analysis of ramensoftware/windhawk@61d99ed8e1 (2026-09-12). Data as JSON: /api/errors/e16322d0cc4055d7. Report an issue: GitHub.

Appendix: source

Thrown at src/windhawk-core/cli/src/commands/data.rs:240

    };
    // A driven install's compile progress, stamped with the mod id.
    if let Some(triple) = payload.get("compileTarget").and_then(Value::as_str) {
        let id = payload.get("modId").and_then(Value::as_str).unwrap_or("");
        logger.info(&format!("Compiling {id} for {}...", arch_label(triple)));
        return;
    }
    let Ok(progress) = serde_json::from_value::<ImportProgress>(payload.clone()) else {
        return;
    };
    let pos = format!("[{}/{}]", progress.index + 1, progress.total);
    let id = &progress.mod_id;
    match progress.status {
        ImportProgressStatus::Installing => logger.info(&format!("{pos} Importing {id}...")),
        ImportProgressStatus::Installed => logger.info(&format!("{pos} Installed {id}")),
        ImportProgressStatus::Skipped => logger.info(&format!("{pos} Skipped {id}")),
        ImportProgressStatus::Failed => {
            let reason = progress.message.as_deref().unwrap_or("import failed");
            logger.warn(&format!("{pos} Failed {id}: {reason}"));
        }
    }
}

/// The selection flag values shared by `data export` and `data import` (their
/// clap arg structs carry an identical set), borrowed into one view so the
/// mapping onto the shared selection shape lives in one place.
struct SelectionFlags<'a> {
    app_settings: bool,
    no_app_settings: bool,
    mods: &'a str,
    settings: bool,
    no_settings: bool,
    config: bool,
    no_config: bool,
    skip_settings: Option<&'a str>,
    with_settings: Option<&'a str>,
    skip_config: Option<&'a str>,

View on GitHub (pinned to 61d99ed8e1)