rust-lang/cargo · error · anyhow::Error

package `{}` cannot be tested because it requires dev-depend

Error message

package `{}` cannot be tested because it requires dev-dependencies and is not a member of the workspace

What it means

Thrown when a build/test intent targets a package that is not a workspace member yet declares dev-dependencies. Cargo cannot build dev-dependencies for non-member packages because dev-deps are only resolved for workspace members, so testing them is impossible. The guard at src/ops/cargo_compile/mod.rs:373-382 checks is_any_test() intent, non-membership, and the presence of non-transitive (dev-only) dependencies before bailing.

Source

Thrown at src/ops/cargo_compile/mod.rs:377

    let to_build_ids = resolve.specs_to_ids(&specs)?;
    // Now get the `Package` for each `PackageId`. This may trigger a download
    // if the user specified `-p` for a dependency that is not downloaded.
    // Dependencies will be downloaded during build_unit_dependencies.
    let mut to_builds = pkg_set.get_many(to_build_ids)?;

    // The ordering here affects some error messages coming out of cargo, so
    // let's be test and CLI friendly by always printing in the same order if
    // there's an error.
    to_builds.sort_by_key(|p| p.package_id());

    for pkg in to_builds.iter() {
        pkg.manifest().print_teapot(gctx);

        if build_config.intent.is_any_test()
            && !ws.is_member(pkg)
            && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
        {
            anyhow::bail!(
                "package `{}` cannot be tested because it requires dev-dependencies \
                 and is not a member of the workspace",
                pkg.name()
            );
        }
    }

    let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
        (Some(args), _) => (Some(args.clone()), "rustc"),
        (_, Some(args)) => (Some(args.clone()), "rustdoc"),
        _ => (None, ""),
    };

    if extra_args.is_some() && to_builds.len() != 1 {
        panic!(
            "`{}` should not accept multiple `-p` flags",
            extra_args_name
        );

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Add the package to the workspace: list its path under [workspace] members in the root Cargo.toml so it becomes a workspace member.
  2. If you cannot make it a member, remove or comment out its [dev-dependencies] (they are unusable for non-members anyway).
  3. Run a non-test intent instead (e.g. `cargo build -p <pkg>`) if you only need a build, not tests.
  4. Enter the dependency's own directory (where it has its own workspace) and run `cargo test` there, where it is the local member.

Example fix

# before (root Cargo.toml)
[workspace]
members = ["crates/main"]
# my-dep is a path dep, not a member; `cargo test -p my-dep` fails

# after
[workspace]
members = ["crates/main", "crates/my-dep"]
Defensive patterns

Strategy: validation

Validate before calling

// Before calling ops::compile with a test intent, verify the target pkg is a member.
use cargo::core::workspace::Workspace;

fn can_test_package(ws: &Workspace, pkg: &cargo::core::Package) -> bool {
    ws.is_member(pkg)
        || !pkg.dependencies().iter().any(|d| !d.is_transitive())
}

// for spec selection: resolve -p names to members first
let bad: Vec<_> = specs.iter()
    .filter(|n| !ws.members().any(|m| m.name().as_str() == n.as_str()))
    .collect();
assert!(bad.is_empty(), "non-members selected for test: {:?}", bad);

Type guard

// Narrow a Package selection to only workspace members that are safe to test.
fn testable_members<'a>(ws: &'a Workspace<'a>) -> impl Iterator<Item = &'a cargo::core::Package> {
    ws.members().filter(|p| p.dependencies().iter().all(|d| d.is_transitive()))
}

Try / catch

// ops::compile returns CargoResult; surface the error to the user verbatim.
match ops::compile(ws, &compile_opts) {
    Ok(_) => {}
    Err(e) if e.to_string().contains("cannot be tested") => {
        eprintln!("package is not a workspace member; add it to [workspace] members");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Running `cargo test -p <pkg>` (or cargo build with a test intent) where <pkg> is a path or git dependency pulled into the package set but not listed in the workspace's members. Concretely, the loop over to_builds hits a pkg with !ws.is_member(pkg) and a dev-dependency, while build_config.intent.is_any_test() is true.

Common situations: Trying to test a forked dependency via a path override (`-p my-dep`) without adding it to the workspace. Using [patch] / path dependencies and attempting to `cargo test -p` on the patched crate directly. Monorepo where a crate was moved out of the members list but tests are still run against it.

Related errors


AI-assisted analysis of rust-lang/cargo@0e07a15537 (2026-08-06). Data as JSON: /data/errors/4b622d7284ce129b.json. Report an issue: GitHub.