Skip to content

cargo add silently drops an earlier duplicate when the same crate is specified twice in one invocation #17320

Description

@GuTS805

Problem

Running cargo add with the same dependency name given twice in one invocation (e.g. with different version requirements) prints an "Adding" status line for both, but the resulting Cargo.toml only contains the last one — with no warning, no error, and exit code 0. The status output is misleading: it claims the first entry was added, when it was actually silently overwritten before the command finished.

Reproduced with a fresh cargo new project:

$ cargo add serde@1.0.229 serde@1.0.200
    Updating crates.io index
      Adding serde v1.0.229 to dependencies
             Features:
             + std
             ...
      Adding serde v1.0.200 to dependencies
             Features:
             + std
             ...
    Updating crates.io index
     Locking 7 packages to latest Rust 1.92.0 compatible versions

$ cat Cargo.toml
[dependencies]
serde = "1.0.200"

Root cause, verified on upstream/master (commit 7759f8c), in src/ops/cargo_add/mod.rs:

  • Lines 94-115: options.dependencies is resolved into a deps list with no dedup/conflict check on crate name.
  • Line 132: for dep in deps { ... } iterates every resolved dep unconditionally.
  • Line 133: print_action_msg(...) prints the "Adding … to dependencies" line for every entry, including ones about to be clobbered.
  • Line 244: manifest.insert_into_table(...) inserts under the dependency's TOML key, so a second entry with the same key silently overwrites the first.

Notably, cargo remove handles the analogous case differently. cargo remove serde serde fails loudly on the second occurrence ("the dependency 'serde' could not be found in 'dependencies'", exit 101), so add and remove aren't even consistent with each other here.

Steps

  1. cargo new demo && cd demo
  2. cargo add serde@1.0.229 serde@1.0.200
  3. cat Cargo.toml. Note only serde = "1.0.200" is present, despite both "Adding" lines being printed, and the command exits 0

Possible Solution(s)

Detect duplicate crate names within a single cargo add invocation (in src/ops/cargo_add/mod.rs, around where options.dependencies is resolved into deps, lines 94-115) and either reject the command with an error, or at minimum emit a warning before silently keeping only the last occurrence. Rather than printing a full "Adding" success line for an entry that's about to be discarded. The exact behavior (hard error vs. warn-and-keep-last vs. merge/reconcile version reqs) is left to maintainer discretion.

Notes

Not a duplicate of #13899 (warns about duplicate versions in the resolved/transitive dependency graph, not repeated names in a single cargo add call), #16101 (conflict between dependencies and dev-dependencies sections, a different scenario), or #15624/#9599 (cargo tree display of transitive duplicates, unrelated command). #16276 fixed a similar class of silent CLI-spec-handling bug in cargo update --breaking, which is useful precedent but doesn't cover cargo add.

Version

cargo 1.92.0 (344c4567c 2025-10-21)
release: 1.92.0
commit-hash: 344c4567c634a25837e3c3476aac08af84cf9203
commit-date: 2025-10-21
host: x86_64-pc-windows-msvc
libgit2: 1.9.1 (sys:0.20.2 vendored)
libcurl: 8.15.0-DEV (sys:0.4.83+curl-8.15.0 vendored ssl:Schannel)
os: Windows 10.0.26200 (Windows 11 CoreSingleLanguage) [64-bit]

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Error and warning messages generated by Cargo itself.C-bugCategory: bugCommand-addS-needs-designStatus: Needs someone to work further on the design for the feature or fix. NOT YET accepted.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions