dbt-labs/dbt-core · error · anyhow

unsupported pre-release lane {lane_str:?} in {semver:?}; acc

Error message

unsupported pre-release lane {lane_str:?} in {semver:?}; accepted: alpha, beta, rc, preview, dev

What it means

parse_release_version only accepts pre-release lanes alpha, beta, rc, preview, dev. When the text before the dot in the pre-release suffix matches none of these lanes, this error is thrown listing the accepted values. It exists to keep the semver-to-PEP 440 mapping deterministic for PyPI.

Source

Thrown at crates/dbt-ci/src/release_version.rs:71

    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:?}");
    }
    Ok(ParsedReleaseVersion {
        base,
        prerelease: Some((lane, number)),
    })
}

pub(crate) fn validate_release_version(semver: &str) -> Result<()> {
    parse_release_version(semver).map(|_| ())
}

pub(crate) fn semver_to_pep440(semver: &str) -> Result<String> {

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Rename the pre-release lane to one of: alpha, beta, rc, preview, dev.
  2. Map custom lanes (e.g. nightly → dev) before passing the version in.
  3. If new lanes are genuinely needed, extend PreReleaseLane::parse rather than passing an unsupported lane.

Example fix

// before
let v = parse_release_version("1.2.3-nightly.7")?;

// after
let v = parse_release_version("1.2.3-dev.7")?;
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED_LANES = {"alpha", "beta", "rc", "preview", "dev"}
pre = version.split("-", 1)[-1].split(".", 1)[0]
assert pre in ALLOWED_LANES, f"lane {pre!r} not in {sorted(ALLOWED_LANES)}"

Type guard

def has_supported_lane(v: str) -> bool:
    return v.split("-", 1)[-1].split(".", 1)[0] in {"alpha", "beta", "rc", "preview", "dev"}

Try / catch

match parse_release_version(&version) {
    Err(e) if e.to_string().contains("unsupported pre-release lane") => map_custom_lane(&version),
    other => other,
}

Prevention

When it happens

Trigger: Calling parse_release_version / semver_to_pep440 / validate_release_version with an unknown lane such as `1.2.3-nightly.1`, `1.2.3-canary.3`, or `1.2.3-snapshot.0`.

Common situations: Teams using CI tools that emit `nightly` or `snapshot` pre-releases; porting conventions from npm (`next`, `canary`) into Python release tooling; typos like `alph.1`.

Related errors


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