dbt-labs/dbt-core · error · anyhow

{filename} declares Requires-Python {actual:?} but the sdist

Error message

{filename} declares Requires-Python {actual:?} but the sdist says {expected:?}; point `--pyproject-dir` at the pyproject that built these wheels

What it means

The wheel's METADATA Requires-Python does not exactly match the requires-python derived from the sdist's pyproject. This guard exists to catch stale or mismatched wheels — usually wheels built from a different pyproject than the one supplied via --pyproject-dir.

Source

Thrown at crates/dbt-ci/src/sdist.rs:151

/// points at, in either direction. pip survives such a mismatch (it builds the
/// wheel and reads the real metadata), but uv trusts the sdist's PKG-INFO and
/// never re-checks — so a wrong `Requires-Python` installs on an unsupported
/// interpreter and dies at import on an ABI symbol, and a missing `Requires-Dist`
/// (in either direction) installs the wrong dependency set and dies on first run.
fn check_wheel_metadata_agrees(spec: &Spec, filename: &str, wheel: &[u8]) -> Result<()> {
    let Some(metadata) = wheel_metadata(wheel)
        .with_context(|| format!("read METADATA from {filename}"))?
        .map(|raw| Metadata::parse(&raw))
        .transpose()
        .with_context(|| format!("parse METADATA from {filename}"))?
    else {
        bail!("{filename} has no *.dist-info/METADATA");
    };

    let expected_python = spec.requires_python.as_deref();
    let actual_python = metadata.requires_python.as_deref();
    if actual_python != expected_python {
        bail!(
            "{filename} declares Requires-Python {actual:?} but the sdist says \
             {expected:?}; point `--pyproject-dir` at the pyproject that built \
             these wheels",
            actual = actual_python.unwrap_or("(absent)"),
            expected = expected_python.unwrap_or("(absent)"),
        );
    }

    let expected_deps: std::collections::BTreeSet<&str> =
        spec.dependencies.iter().map(|d| d.trim()).collect();
    let actual_deps: std::collections::BTreeSet<&str> =
        metadata.requires_dist.iter().map(|d| d.trim()).collect();
    if expected_deps != actual_deps {
        bail!(
            "{filename}'s Requires-Dist disagrees with the sdist: sdist deps={:?}, \
             wheel deps={:?}",
            expected_deps,
            actual_deps,

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Rebuild all wheels from the pyproject currently passed via --pyproject-dir so Requires-Python matches the sdist.
  2. Pass --pyproject-dir at the pyproject that actually built these wheels, as the message suggests.
  3. Align requires-python in pyproject.toml across the wheel and sdist builds (e.g. after a version bump, rebuild everything).

Example fix

# before
pyproject.toml (wheels): requires-python = ">=3.9"
pyproject.toml (--pyproject-dir): requires-python = ">=3.8"
# after
set both to requires-python = ">=3.9" and rebuild the wheels
Defensive patterns

Strategy: validation

Validate before calling

# compare Requires-Python between wheel metadata and pyproject before publishing
diff <(unzip -p wheel/*.whl '*/METADATA' | grep '^Requires-Python:') \
     <(grep '^requires-python' pyproject.toml)

Try / catch

if let Err(e) = check_wheel_metadata_agrees(...) {
    if e.to_string().contains("Requires-Python") {
        eprintln!("rebuild wheels from the --pyproject-dir pyproject: {e:#}");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: check_wheel_metadata_agrees (via build_release_sdist) compares metadata.requires_python against spec.requires_python and bails on any inequality, including one side being absent.

Common situations: Wheels built before a requires-python bump in pyproject.toml were cached and reused; --pyproject-dir points at the wrong project; one artifact set omits Requires-Python entirely.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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