{"record":{"id":"786f1cb22db3d01b","repo":"dbt-labs/dbt-core","slug":"expected-x-y-z-got-base-in-semver","errorCode":null,"errorMessage":"expected X.Y.Z, got {base:?} in {semver:?}","messagePattern":"expected X\\.Y\\.Z, got (.+?) in (.+?)","errorType":"validation","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"crates/dbt-ci/src/release_version.rs","lineNumber":59,"sourceCode":"        }\n    }\n}\n\npub(crate) fn parse_release_version(semver: &str) -> Result<ParsedReleaseVersion<'_>> {\n    if semver.contains('+') {\n        bail!(\"SemVer build metadata not supported in {semver:?}\");\n    }\n    let (base, pre) = match semver.split_once('-') {\n        Some((b, p)) => (b, Some(p)),\n        None => (semver, None),\n    };\n    let parts: Vec<&str> = base.split('.').collect();\n    let well_formed = parts.len() == 3\n        && parts\n            .iter()\n            .all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()));\n    if !well_formed {\n        bail!(\"expected X.Y.Z, got {base:?} in {semver:?}\");\n    }\n    let Some(pre) = pre else {\n        return Ok(ParsedReleaseVersion {\n            base,\n            prerelease: None,\n        });\n    };\n    let (lane_str, number) = pre\n        .split_once('.')\n        .ok_or_else(|| anyhow!(\"expected `<lane>.N` after `-`, got {pre:?} in {semver:?}\"))?;\n    let lane = PreReleaseLane::parse(lane_str).ok_or_else(|| {\n        anyhow!(\n            \"unsupported pre-release lane {lane_str:?} in {semver:?}; \\\n             accepted: alpha, beta, rc, preview, dev\"\n        )\n    })?;\n    if number.is_empty() || !number.chars().all(|c| c.is_ascii_digit()) {\n        bail!(\"expected integer after `{lane_str}.`, got {number:?} in {semver:?}\");","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-ci/src/release_version.rs#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nparse_release_version(\"v1.2\")?;\n// after\nlet v = version.trim_start_matches('v');\nlet v = format!(\"{}.0\", v); // \"1.2\" -> \"1.2.0\"\nparse_release_version(&v)?;","handlingStrategy":"validation","validationCode":"fn validate_semver_base(v: &str) -> Result<(), String> {\n    let base = v.trim_start_matches('v').split('-').next().unwrap();\n    let ok = base.split('.').count() == 3\n        && base.split('.').all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit()));\n    if ok { Ok(()) } else { Err(format!(\"{v:?} is not X.Y.Z\")) }\n}","typeGuard":null,"tryCatchPattern":"let v = version.trim_start_matches('v');\nparse_release_version(v).map_err(|e| {\n    eprintln!(\"bad release version {v:?}: {e}\");\n    e\n})?;","preventionTips":["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."],"tags":["semver","versioning","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}