rust-lang/cargo · error

replacements must specify a version to replace, but

Error message

replacements must specify a version to replace, but `{}` does not

What it means

Thrown in the [replace] section parser when a PackageIdSpec used as a replacement key lacks a version. Replacements must pin the exact version to replace (e.g. 'my-crate:1.0.0'), so spec.version() returning None is fatal. The spec string is interpolated. This is separate from the check that disallows a version *requirement* inside the replacement dependency itself.

Solutions

  1. Add the version to the replace key: [replace."my-crate:1.0.0"].
  2. Consider migrating to [patch] which is the modern, recommended replacement mechanism.
  3. Verify the version matches the one you want to override exactly (replace pins, it does not range).

Example fix

# before
[replace]
"my-crate" = { path = "../my-crate" }

# after
[replace]
"my-crate:1.0.0" = { path = "../my-crate" }
Defensive patterns

Strategy: validation

Validate before calling

fn replace_key_has_version(key: &str) -> Result<(), String> {
    // PackageIdSpec keys must be name:version
    if !key.contains(':') {
        return Err(format!("replace key `{key}` must include a version, e.g. `{key}:1.0.0`"));
    }
    Ok(())
}

Type guard

fn replace_key_is_versioned(key: &str) -> bool {
    key.split(':').count() == 2 && key.split(':').nth(1).map(|v| !v.is_empty()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Writing [replace."my-crate"] without a version qualifier in Cargo.toml. PackageIdSpec::parse succeeds (it accepts a bare name) but spec.version() is None, hitting the ok_or_else.

Common situations: A developer uses [replace] for the first time and omits the version. Confusing [patch] (which takes a URL key) with [replace] (which takes a name:version key). Stale documentation showing replace syntax without a version.

Related errors


AI-assisted analysis of rust-lang/cargo@98a09e7e7d (2026-08-11). Data as JSON: /api/errors/da80fdfc03aabfc6. Report an issue: GitHub.

Appendix: source

Thrown at src/workspace/parser/mod.rs:2161

                     version to replace, but `{}` does not",
                spec
            )
        })?;
        if spec.url().is_none() {
            spec.set_url(CRATES_IO_INDEX.parse().unwrap());
        }

        if replacement.is_version_specified() {
            bail!(
                "replacements cannot specify a version \
                     requirement, but found one for `{}`",
                spec
            );
        }

        let mut dep = dep_to_dependency(replacement, spec.name(), manifest_ctx, None)?;
        let version = spec.version().ok_or_else(|| {
            anyhow!(
                "replacements must specify a version \
                     to replace, but `{}` does not",
                spec
            )
        })?;
        unused_dep_keys(
            dep.name_in_toml().as_str(),
            "replace",
            replacement.unused_keys(),
            &mut manifest_ctx.warnings,
        );
        dep.set_version_req(OptVersionReq::exact(&version));
        replace.push((spec, dep));
    }
    Ok(replace)
}

fn patch(

View on GitHub (pinned to 98a09e7e7d)