{"record":{"id":"f29cc8fc83ef0ecb","repo":"rust-lang/cargo","slug":"cannot-specify-both-metabuild-and-build","errorCode":null,"errorMessage":"cannot specify both `metabuild` and `build`","messagePattern":"cannot specify both `metabuild` and `build`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/workspace/parser/targets.rs","lineNumber":105,"sourceCode":"\n    targets.extend(to_test_targets(\n        normalized_toml.test.as_deref().unwrap_or_default(),\n        package_root,\n        edition,\n        warnings,\n    )?);\n\n    targets.extend(to_bench_targets(\n        normalized_toml.bench.as_deref().unwrap_or_default(),\n        package_root,\n        edition,\n        warnings,\n    )?);\n\n    // processing the custom build script\n    if let Some(custom_build) = package.normalized_build().expect(\"previously normalized\") {\n        if metabuild.is_some() {\n            anyhow::bail!(\"cannot specify both `metabuild` and `build`\");\n        }\n        validate_unique_build_scripts(custom_build)?;\n        for script in custom_build {\n            let script_path = Path::new(script);\n            let name = format!(\n                \"build-script-{}\",\n                script_path\n                    .file_stem()\n                    .and_then(|s| s.to_str())\n                    .expect(\"previously normalized\")\n            );\n            targets.push(Target::custom_build_target(\n                &name,\n                package_root.join(script_path),\n                edition,\n            ));\n        }\n    }","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/rust-lang/cargo/blob/eb98b54bc9f3c74519f43d066cb3fd02ebc88df0/src/workspace/parser/targets.rs#L87-L123","documentation":"Cargo supports two mutually-exclusive build script mechanisms: a traditional `build.rs` (via `package.build`) and `metabuild` (a list of build-dependency crates that generate build scripts). The parser reads `package.normalized_build()` and checks `metabuild.is_some()` in the same branch; if both are present it bails immediately. Only one system may drive the build.","triggerScenarios":"Cargo.toml sets `[package] build = \"build.rs\"` (or relies on the default `build.rs` convention) AND also declares `[package] metabuild = [\"some-crate\"]`. The `if let Some(custom_build) ... { if metabuild.is_some() { bail! } }` block at targets.rs:100-105 fires.","commonSituations":"Migrating a crate from a hand-written build.rs to metabuild and forgetting to remove the old `build` key. Copy-pasting a metabuild example into a crate that already has a build script. Tooling or scaffolding templates that set both.","solutions":["Remove the `build` key (or delete build.rs) if you intend to use `metabuild`.","Remove the `metabuild` key if you intend to keep your traditional build script.","Run `cargo check` after editing to confirm only one mechanism remains."],"exampleFix":"# before\n[package]\nbuild = \"build.rs\"\nmetabuild = [\"metabuild-crate\"]\n\n# after (metabuild chosen)\n[package]\nmetabuild = [\"metabuild-crate\"]","handlingStrategy":"validation","validationCode":"# Pre-flight TOML check before invoking cargo\nuse toml::Value;\nfn has_both_build_and_metabuild(manifest: &Value) -> bool {\n    let pkg = manifest.get(\"package\");\n    pkg.and_then(|p| p.get(\"build\")).is_some()\n        && pkg.and_then(|p| p.get(\"metabuild\")).is_some()\n}\n// assert!(!has_both_build_and_metabuild(&parsed));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat `build` and `metabuild` as mutually exclusive in manifest linters.","When migrating to metabuild, remove the `build` key in the same commit.","Run `cargo check` in CI to catch manifest conflicts early."],"tags":["cargo","config","build-script","metabuild","manifest"],"backgroundTag":null,"analyzedSha":"eb98b54bc9f3c74519f43d066cb3fd02ebc88df0","analyzedAt":"2026-08-11T17:42:36.556Z","contentChangedAt":"2026-08-11T17:42:36.556Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}