dbt-labs/dbt-core · error · anyhow
expected X.Y.Z, got {base:?} in {semver:?}
Error message
expected X.Y.Z, got {base:?} in {semver:?} What it means
parse_release_version requires the base (non-prerelease) part of the version to be exactly three non-empty all-digit components (X.Y.Z). This bail fires when the base doesn't match that shape, e.g. missing components, extra components, or non-numeric parts.
Source
Thrown at crates/dbt-ci/src/release_version.rs:59
}
}
}
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,
});
};
let (lane_str, number) = pre
.split_once('.')
.ok_or_else(|| anyhow!("expected `<lane>.N` after `-`, got {pre:?} in {semver:?}"))?;
let lane = PreReleaseLane::parse(lane_str).ok_or_else(|| {
anyhow!(
"unsupported pre-release lane {lane_str:?} in {semver:?}; \
accepted: alpha, beta, rc, preview, dev"
)
})?;
if number.is_empty() || !number.chars().all(|c| c.is_ascii_digit()) {
bail!("expected integer after `{lane_str}.`, got {number:?} in {semver:?}");View on GitHub (pinned to 0267ce9170)
Solutions
- Change the version to exactly three numeric components, e.g. "1.2.3".
- Strip any leading "v" prefix or other decoration before passing the version.
- Fix the CI/release tooling so it emits a full three-part version.
Example fix
// before
parse_release_version("v1.2")?;
// after
let v = version.trim_start_matches('v');
let v = format!("{}.0", v); // "1.2" -> "1.2.0"
parse_release_version(&v)?; Defensive patterns
Strategy: validation
Validate before calling
fn validate_semver_base(v: &str) -> Result<(), String> {
let base = v.trim_start_matches('v').split('-').next().unwrap();
let ok = base.split('.').count() == 3
&& base.split('.').all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()));
if ok { Ok(()) } else { Err(format!("{v:?} is not X.Y.Z")) }
} Try / catch
let v = version.trim_start_matches('v');
parse_release_version(v).map_err(|e| {
eprintln!("bad release version {v:?}: {e}");
e
})?; Prevention
- Always emit exactly three numeric components in release versions.
- Strip 'v' prefixes when deriving versions from git tags.
- Validate versions at the start of the release workflow, before artifact builds.
When it happens
Trigger: Calling parse_release_version (or validate_release_version / semver_to_pep440) with versions like "1.2", "1.2.3.4", "v1.2.3", "1.2.x", or "1.2.3-" whose base is not three integer components.
Common situations: Hand-edited version strings in release configs, a leading 'v' prefix copied from a git tag, two-component versions from simplified versioning schemes, or template placeholders left unsubstituted in CI.
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
- SemVer build metadata not supported 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/786f1cb22db3d01b.
Report an issue: GitHub.