ramensoftware/windhawk · warning

the mod has no recorded version, so it was not exported

Error message

the mod has no recorded version, so it was not exported

What it means

During user-data export, a mod whose installed entry has an empty recorded version cannot be represented in the archive (ArchiveMod requires a version), so it is skipped and a per-mod ExportWarning with this message is emitted. Export continues for other mods.

Solutions

  1. Reinstall the mod so its version is recorded, then re-export
  2. Ensure the mod's source carries a proper `// ==WindhawkMod== @version` header
  3. Drop the mod from the export selection
  4. Verify the mod shows a version in the Windhawk UI before exporting

Example fix

// before
// ==WindhawkMod==
// @id my-mod
// ==/WindhawkMod==
// after
// ==WindhawkMod==
// @id my-mod
// @version 1.0.1
// ==/WindhawkMod==
Defensive patterns

Strategy: validation

Validate before calling

// before export: check every selected mod has a recorded version
for m in installed_mods.filter(|m| selection.includes(&m.id)) {
    if m.version.is_empty() { eprintln!("{}: no version, will be skipped", m.id); }
}

Type guard

fn has_version(entry: &InstalledModListEntry) -> bool {
    !entry.version.as_deref().unwrap_or("").is_empty()
}

Prevention

When it happens

Trigger: Running `windhawk data export` while selecting a mod whose storage entry lacks a version — e.g. an install that never completed version recording or a hand-edited config mirror missing @version.

Common situations: Crashed or interrupted mod installs; mods installed by old versions that didn't record the version; corrupted mod storage state.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at src/windhawk-core/core/src/services/user_data.rs:193

}

/// Build one mod's archive entry, or `None` when it cannot be represented (a
/// local mod with no usable source, a mod with no recorded version, or an entry
/// the archive format itself refuses); a per-mod issue is pushed onto
/// `warnings` in every case.
fn export_mod(
    session: &SessionInner,
    selection: &UserDataSelection,
    offline: bool,
    language: &str,
    id: &str,
    entry: &InstalledModListEntry,
    warnings: &mut Vec<ExportWarning>,
) -> Result<Option<ArchiveMod>, CoreError> {
    let is_local = ModId::str_is_local(id);
    let version = installed_version(entry);
    if version.is_empty() {
        warnings.push(warn(
            id,
            "the mod has no recorded version, so it was not exported",
        ));
        return Ok(None);
    }
    let name = entry.metadata.as_ref().and_then(|m| m.name.clone());

    let (want_settings, want_config) = facet_toggles(selection, id);
    // A repository mod's source embeds only under an offline export; a local
    // mod's source is always embedded (it exists nowhere else). The source is
    // read locally whenever settings are selected, to canonicalize against its
    // declared types, even when it is not embedded.
    let embed = is_local || offline;
    let need_source = embed || want_settings;
    let mut source_text = None;
    if need_source {
        match read_source(session, id)? {
            SourceRead::Text(text) => source_text = Some(text),

View on GitHub (pinned to 61d99ed8e1)