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
- Add the version to the replace key: [replace."my-crate:1.0.0"].
- Consider migrating to [patch] which is the modern, recommended replacement mechanism.
- 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
- Always include the exact version in [replace] keys: "name:version".
- Prefer [patch] over [replace] for modern manifests.
- Review replace syntax against current cargo docs before use.
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
- dependency ( ) specified without providing a local path…
- `lints. . ` is not a valid lint name
- `lints. . ` is not valid lint name; try `lints. .
- manifest is missing either a `[package]` or a `[workspace]`
- can only edit absolute paths, got
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)