dbt-labs/dbt-core · error · anyhow
SemVer build metadata not supported in {semver:?}
Error message
SemVer build metadata not supported in {semver:?} What it means
parse_release_version in crates/dbt-ci/src/release_version.rs rejects any SemVer string containing '+' because build metadata has no representation in PEP 440 release versions used by PyPI. The error exists to force callers to strip build metadata before converting a SemVer version to a Python release version.
Source
Thrown at crates/dbt-ci/src/release_version.rs:47
}
impl ParsedReleaseVersion<'_> {
pub(crate) fn to_pep440(&self) -> String {
let Some((lane, n)) = self.prerelease else {
return self.base.to_string();
};
match lane {
PreReleaseLane::Alpha => format!("{}a{n}", self.base),
PreReleaseLane::Beta => format!("{}b{n}", self.base),
PreReleaseLane::Rc => format!("{}rc{n}", self.base),
PreReleaseLane::Dev => format!("{}.dev{n}", self.base),
}
}
}
pub(crate) fn parse_release_version(semver: &str) -> Result<ParsedReleaseVersion<'_>> {
if semver.contains('+') {
bail!("SemVer build metadata not supported in {semver:?}");
}
let (base, pre) = match semver.split_once('-') {
Some((b, p)) => (b, Some(p)),
None => (semver, None),
};
let parts: Vec<&str> = base.split('.').collect();
let well_formed = parts.len() == 3
&& parts
.iter()
.all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()));
if !well_formed {
bail!("expected X.Y.Z, got {base:?} in {semver:?}");
}
let Some(pre) = pre else {
return Ok(ParsedReleaseVersion {
base,
prerelease: None,
});View on GitHub (pinned to 0267ce9170)
Solutions
- Remove the '+' build-metadata suffix from the version string before invoking the release pipeline.
- If the metadata matters, encode the equivalent information in a pre-release lane (e.g. 1.2.3-rc.1) instead.
- Adjust your release tooling (e.g. cargo release config or CI version step) to emit plain X.Y.Z[-pre.N] versions.
Example fix
// before
let version = "1.2.3+gdeadbeef";
parse_release_version(version)?;
// after
let version = version.split('+').next().unwrap(); // "1.2.3"
parse_release_version(version)?; Defensive patterns
Strategy: validation
Validate before calling
fn validate_no_build_metadata(semver: &str) -> Result<(), String> {
if semver.contains('+') {
return Err(format!("strip '+' build metadata from {semver:?}"));
}
Ok(())
} Type guard
fn is_plain_release_version(v: &str) -> bool { !v.contains('+') } Try / catch
match parse_release_version(v) {
Ok(parsed) => use(parsed),
Err(e) if e.to_string().contains("build metadata") => {
let stripped = v.split('+').next().unwrap();
parse_release_version(stripped)?
}
Err(e) => return Err(e),
} Prevention
- Configure version tooling to never emit '+' build metadata in release versions.
- Strip build metadata at the CI boundary where the version is first produced.
- Validate the version string early in the pipeline, before builds run.
When it happens
Trigger: Calling parse_release_version, validate_release_version, or semver_to_pep440 with a version like "1.2.3+build.5" or "1.2.3+abc123" (typically a version auto-generated with a git SHA suffix).
Common situations: CI injects build metadata into the version (common with cargo-release or setuptools_scm-style schemes) and that version is passed directly to the release pipeline; developers copy a local dev version into the release config.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- expected X.Y.Z, got {base:?} in {semver:?}
- expected integer after `{lane_str}.`, got {number:?} in {sem
- unsupported pre-release lane {lane_str:?} in {semver:?}; acc
- expected `<lane>.N` after `-`, got {pre:?} in {semver:?}
- formula file not found: {}
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/d1697ee800b84e98.
Report an issue: GitHub.