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

  1. Remove the '+' build-metadata suffix from the version string before invoking the release pipeline.
  2. If the metadata matters, encode the equivalent information in a pre-release lane (e.g. 1.2.3-rc.1) instead.
  3. 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

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


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/d1697ee800b84e98. Report an issue: GitHub.