{"record":{"id":"c16a81739fab2068","repo":"dbt-labs/dbt-core","slug":"expected-integer-after-lane-str-got-number","errorCode":null,"errorMessage":"expected integer after `{lane_str}.`, got {number:?} in {semver:?}","messagePattern":"expected integer after `(.+?)\\.`, got (.+?) in (.+?)","errorType":"validation","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"crates/dbt-ci/src/release_version.rs","lineNumber":77,"sourceCode":"        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:?}\");\n    }\n    Ok(ParsedReleaseVersion {\n        base,\n        prerelease: Some((lane, number)),\n    })\n}\n\npub(crate) fn validate_release_version(semver: &str) -> Result<()> {\n    parse_release_version(semver).map(|_| ())\n}\n\npub(crate) fn semver_to_pep440(semver: &str) -> Result<String> {\n    parse_release_version(semver).map(|p| p.to_pep440())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-ci/src/release_version.rs#L59-L95","documentation":"For pre-release versions (X.Y.Z-lane.N), parse_release_version accepts only lanes alpha, beta, rc, preview, dev, and requires a non-empty integer after the lane's dot. This bail fires when the number portion after the lane is empty or contains non-digit characters.","triggerScenarios":"Calling parse_release_version with a prerelease like \"1.2.3-alpha.\", \"1.2.3-beta.x\", \"1.2.3-rc.01a\", or \"1.2.3-rc.1.2\" where the segment after 'rc.' is not a pure integer.","commonSituations":"Using SemVer dot-separated prerelease identifiers (e.g. -rc.1.2 or -alpha.beta) that this stricter scheme doesn't accept, typos in manually set versions, or versions generated by tools emitting multi-part prerelease numbers.","solutions":["Make the prerelease number a single non-negative integer, e.g. \"1.2.3-rc.1\".","Ensure the lane is one of alpha, beta, rc, preview, dev (a different lane produces the preceding anyhow error).","Normalize your version-generation tool to emit single-integer prerelease suffixes."],"exampleFix":"// before\nparse_release_version(\"1.2.3-rc.1.2\")?;\n// after\nparse_release_version(\"1.2.3-rc.2\")?; // collapse to single integer","handlingStrategy":"validation","validationCode":"fn validate_prerelease(v: &str) -> Result<(), String> {\n    if let Some((_, pre)) = v.split_once('-') {\n        let mut it = pre.splitn(2, '.');\n        let lane = it.next().unwrap_or(\"\");\n        if !matches!(lane, \"alpha\"|\"beta\"|\"rc\"|\"preview\"|\"dev\") {\n            return Err(format!(\"unsupported lane {lane:?}\"));\n        }\n        match it.next() {\n            Some(n) if !n.is_empty() && n.chars().all(|c| c.is_ascii_digit()) => Ok(()),\n            other => Err(format!(\"prerelease number must be an integer, got {other:?}\")),\n        }\n    } else { Ok(()) }\n}","typeGuard":null,"tryCatchPattern":"parse_release_version(v).map_err(|e| {\n    eprintln!(\"invalid prerelease in {v:?}: {e}; use e.g. 1.2.3-rc.1\");\n    e\n})?;","preventionTips":["Use single-integer prerelease suffixes: -alpha.1, -beta.2, -rc.1.","Restrict version-generation tools to the accepted lanes.","Run version validation as an early CI gate."],"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"}