{"record":{"id":"c206b52b69cffca1","repo":"dbt-labs/dbt-core","slug":"filename-declares-requires-python-actual-but","errorCode":null,"errorMessage":"{filename} declares Requires-Python {actual:?} but the sdist says {expected:?}; point `--pyproject-dir` at the pyproject that built these wheels","messagePattern":"(.+?) declares Requires-Python (.+?) but the sdist says (.+?); point `--pyproject-dir` at the pyproject that built these wheels","errorType":"validation","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"crates/dbt-ci/src/sdist.rs","lineNumber":151,"sourceCode":"/// points at, in either direction. pip survives such a mismatch (it builds the\n/// wheel and reads the real metadata), but uv trusts the sdist's PKG-INFO and\n/// never re-checks — so a wrong `Requires-Python` installs on an unsupported\n/// interpreter and dies at import on an ABI symbol, and a missing `Requires-Dist`\n/// (in either direction) installs the wrong dependency set and dies on first run.\nfn check_wheel_metadata_agrees(spec: &Spec, filename: &str, wheel: &[u8]) -> Result<()> {\n    let Some(metadata) = wheel_metadata(wheel)\n        .with_context(|| format!(\"read METADATA from {filename}\"))?\n        .map(|raw| Metadata::parse(&raw))\n        .transpose()\n        .with_context(|| format!(\"parse METADATA from {filename}\"))?\n    else {\n        bail!(\"{filename} has no *.dist-info/METADATA\");\n    };\n\n    let expected_python = spec.requires_python.as_deref();\n    let actual_python = metadata.requires_python.as_deref();\n    if actual_python != expected_python {\n        bail!(\n            \"{filename} declares Requires-Python {actual:?} but the sdist says \\\n             {expected:?}; point `--pyproject-dir` at the pyproject that built \\\n             these wheels\",\n            actual = actual_python.unwrap_or(\"(absent)\"),\n            expected = expected_python.unwrap_or(\"(absent)\"),\n        );\n    }\n\n    let expected_deps: std::collections::BTreeSet<&str> =\n        spec.dependencies.iter().map(|d| d.trim()).collect();\n    let actual_deps: std::collections::BTreeSet<&str> =\n        metadata.requires_dist.iter().map(|d| d.trim()).collect();\n    if expected_deps != actual_deps {\n        bail!(\n            \"{filename}'s Requires-Dist disagrees with the sdist: sdist deps={:?}, \\\n             wheel deps={:?}\",\n            expected_deps,\n            actual_deps,","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-ci/src/sdist.rs#L133-L169","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Rebuild all wheels from the pyproject currently passed via --pyproject-dir so Requires-Python matches the sdist.","Pass --pyproject-dir at the pyproject that actually built these wheels, as the message suggests.","Align requires-python in pyproject.toml across the wheel and sdist builds (e.g. after a version bump, rebuild everything)."],"exampleFix":"# before\npyproject.toml (wheels): requires-python = \">=3.9\"\npyproject.toml (--pyproject-dir): requires-python = \">=3.8\"\n# after\nset both to requires-python = \">=3.9\" and rebuild the wheels","handlingStrategy":"validation","validationCode":"# compare Requires-Python between wheel metadata and pyproject before publishing\ndiff <(unzip -p wheel/*.whl '*/METADATA' | grep '^Requires-Python:') \\\n     <(grep '^requires-python' pyproject.toml)","typeGuard":null,"tryCatchPattern":"if let Err(e) = check_wheel_metadata_agrees(...) {\n    if e.to_string().contains(\"Requires-Python\") {\n        eprintln!(\"rebuild wheels from the --pyproject-dir pyproject: {e:#}\");\n    }\n    return Err(e);\n}","preventionTips":["Rebuild all wheels whenever requires-python changes in pyproject.toml.","Always pass the same pyproject directory that built the wheels via --pyproject-dir.","Keep one canonical pyproject.toml; avoid stale copies in CI caches.","Run the cross-check locally before publishing."],"tags":["wheel","metadata","requires-python","packaging"],"backgroundTag":"schema-validation-failed","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"}