{"id":"824ba8ea63d57b08","repo":"rust-lang/cargo","slug":"the-manifest-file-needs-to-be-updated-but-lock","errorCode":null,"errorMessage":"the manifest file {} needs to be updated but {locked_flag} was passed to prevent this","messagePattern":"the manifest file (.+?) needs to be updated but (.+?) was passed to prevent this","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/ops/cargo_add/mod.rs","lineNumber":288,"sourceCode":"        }\n        manifest.gc_dep(dep.toml_key());\n    }\n\n    if was_sorted {\n        if let Some(table) = manifest\n            .get_table_mut(&dep_table)\n            .and_then(TomlItem::as_table_like_mut)\n        {\n            table.sort_values();\n        }\n    }\n\n    manifest.ensure_edition();\n\n    if let Some(locked_flag) = options.gctx.locked_flag() {\n        let new_raw_manifest = manifest.to_string();\n        if original_raw_manifest != new_raw_manifest {\n            anyhow::bail!(\n                \"the manifest file {} needs to be updated but {locked_flag} was passed to prevent this\",\n                manifest.path.display()\n            );\n        }\n    }\n\n    if options.dry_run {\n        options.gctx.shell().warn(\"aborting add due to dry run\")?;\n    } else {\n        manifest.write()?;\n    }\n\n    Ok(())\n}\n\n/// Dependency entry operation\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct DepOp {","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/rust-lang/cargo/blob/0e07a155371a6ce88ae53a2c00df940280c09a67/src/ops/cargo_add/mod.rs#L270-L306","documentation":"When `--locked` (or the equivalent `locked_flag`) is in effect, cargo-add refuses to modify `Cargo.toml` even though adding the dependency requires writing to it. The function snapshots the raw manifest before processing, applies edits in memory, and if `original_raw_manifest != new_raw_manifest` under a locked flag it bails. This guarantees reproducible, frozen manifests in CI.","triggerScenarios":"Running `cargo add <dep> --locked`, or any `cargo add` invocation while `.cargo/config.toml` / environment forces locked mode. Any dependency addition mutates the manifest, so the diff is always non-empty and the bail fires.","commonSituations":"CI pipelines that pass `--locked` globally to all cargo subcommands, or Dockerfile lines that bake `--locked` into a generic cargo wrapper. The flag is meaningful for `build`/`update` but conflicts with an *additive* manifest operation.","solutions":["Drop `--locked` from the `cargo add` invocation; adding a dependency is inherently a manifest mutation.","If reproducibility is required, run `cargo add` without `--locked`, commit the new `Cargo.toml` and `Cargo.lock`, and keep `--locked` for subsequent `cargo build`/`cargo generate-lockfile` steps.","Audit `.cargo/config.toml` and your CI matrix for a global `locked = true` or wrapper that injects `--locked`."],"exampleFix":"# before (CI)\ncargo add serde --locked\n\n# after\ncargo add serde\ngit commit -am \"add serde\"","handlingStrategy":"validation","validationCode":"// Never pass --locked to an additive operation. Strip it before invoking cargo add.\nfn sanitize_add_args(args: &mut Vec<String>) {\n    if args.iter().any(|a| a == \"add\") {\n        args.retain(|a| a != \"--locked\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep `--locked` scoped to read/lock operations (`build`, `generate-lockfile`), not `add`.","In CI, separate the dependency-bootstrap step from the locked-build step.","Document in your Makefile which targets are lock-affecting vs lock-consuming."],"tags":["cargo-add","locked","ci","manifest"],"analyzedSha":"0e07a155371a6ce88ae53a2c00df940280c09a67","analyzedAt":"2026-08-06T01:46:58.334Z","schemaVersion":2}