{"record":{"id":"13a23d32ff8b835e","repo":"dbt-labs/dbt-core","slug":"filename-has-no-dist-info-metadata","errorCode":null,"errorMessage":"{filename} has no *.dist-info/METADATA","messagePattern":"(.+?) has no \\*\\.dist-info/METADATA","errorType":"validation","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"crates/dbt-ci/src/sdist.rs","lineNumber":145,"sourceCode":"    }\n\n    build_sdist(spec, &version_pep440, &wheels, base_url, out_dir)\n}\n\n/// Fails the release when the sdist's static metadata disagrees with a wheel it\n/// 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();","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-ci/src/sdist.rs#L127-L163","documentation":"check_wheel_metadata_agrees expects every candidate wheel to contain a *.dist-info/METADATA file inside its zip. If wheel_metadata returns None (no METADATA entry found), the function bails because Requires-Python and Requires-Dist cross-checks are impossible without it.","triggerScenarios":"Passing a file to build_release_sdist / check_wheel_metadata_agrees that is not a valid wheel (no .dist-info/METADATA in the zip): a raw .tar.gz, a corrupted/truncated wheel, or a zip built without dist-info.","commonSituations":"Pointing --target/asset globs at artifacts that aren't wheels, a build step producing broken wheels, or manually renamed/zipped files being fed to the cross-check.","solutions":["Verify the file is a proper wheel (unzip -l FILE | grep 'dist-info/METADATA') and rebuild it with a real wheel builder if not.","Ensure you are passing wheel files (.whl), not sdists or plain zips, as targets.","Re-run the wheel build (maturin/cibuildwheel) if the wheel was truncated during upload/download (also check the recorded sha256)."],"exampleFix":"# before\nunzip -l dbt_core-1.0.0.zip   # no dist-info/METADATA -> error\n# after\nmaturin build --release       # produce a real .whl and pass that path","handlingStrategy":"validation","validationCode":"# verify each wheel contains METADATA before handing it to the pipeline\nfor whl in wheels/*.whl; do\n  unzip -l \"$whl\" | grep -q 'dist-info/METADATA' || { echo \"$whl has no METADATA\" >&2; exit 1; }\ndone","typeGuard":"fn looks_like_wheel(path: &Path) -> bool {\n    path.extension().map_or(false, |e| e == \"whl\")\n}","tryCatchPattern":"match check_wheel_metadata_agrees(...) {\n    Err(e) if e.to_string().contains(\"no *.dist-info/METADATA\") => {\n        eprintln!(\"not a valid wheel; rebuild it: {e:#}\");\n        std::process::exit(1);\n    }\n    r => r,\n}","preventionTips":["Only feed files produced by a real wheel builder (maturin/cibuildwheel) into the pipeline.","Verify wheel integrity with sha256 after download/upload.","Glob asset inputs narrowly (*.whl) so sdists/zips cannot slip in."],"tags":["wheel","validation","packaging"],"backgroundTag":"unexpected-response-shape","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"}