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

invalid package name `{}`: it is a Rust keyword{}

Error message

invalid package name `{}`: it is a Rust keyword{}

What it means

check_name rejects a package name that exactly matches a Rust keyword (detected via restricted_names::is_keyword). Keywords like `fn`, `match`, `crate`, `impl`, `type`, `pub` cannot be used as a crate name because they would collide with the language grammar when the crate is referenced as an identifier. The trailing {help} offers a [[bin]] override path when applicable.

Source

Thrown at src/ops/cargo_new.rs:210

                This can be done by setting the binary filename to `src/bin/{name}.rs` \
                or change the name in Cargo.toml with:\n\
                \n    \
                [[bin]]\n    \
                name = \"{name}\"\n    \
                path = \"src/main.rs\"\n\
            ",
                name = name
            ));
        }
        help
    };
    PackageName::new(name).map_err(|err| {
        let help = bin_help();
        anyhow::anyhow!("{err}{help}")
    })?;

    if restricted_names::is_keyword(name) {
        anyhow::bail!(
            "invalid package name `{}`: it is a Rust keyword{}",
            name,
            bin_help()
        );
    }
    if restricted_names::is_conflicting_artifact_name(name) {
        if has_bin {
            anyhow::bail!(
                "invalid package name `{}`: \
                it conflicts with cargo's build directory names{}",
                name,
                name_help
            );
        } else {
            shell.warn(format!(
                "package `{}` will not support binary \
                executables with that name, \
                it conflicts with cargo's build directory names",

View on GitHub (pinned to 0e07a15537)

Solutions

  1. Pick a non-keyword name, e.g. `match` -> `matcher` or `match-utils`
  2. Use `--name <non-keyword>` to override the directory-derived name
  3. Rename the containing directory before running cargo new

Example fix

# before
cargo new match
# after
cargo new matcher   # or: cargo new match --name matcher
Defensive patterns

Strategy: validation

Validate before calling

const KEYWORDS: &[&str] = &[
    "as","break","const","continue","crate","dyn","else","enum","extern","false","fn","for",
    "if","impl","in","let","loop","match","mod","move","mut","pub","ref","return","self","Self",
    "static","struct","super","trait","true","type","unsafe","use","where","while","async","await",
];
fn is_rust_keyword(name: &str) -> bool { KEYWORDS.contains(&name) }

Type guard

const KEYWORDS = new Set(['fn','match','crate','impl','type','pub','let','if','else','for','while','loop','return','struct','enum','trait','mod','use','move','self','Self','super','static','const','unsafe','as','in','ref','dyn','where','true','false','extern','break','continue','mut','async','await']);
function isNotKeyword(name: string): boolean { return !KEYWORDS.has(name); }

Prevention

When it happens

Trigger: `cargo new match`, `cargo new --name crate`, or running `cargo new` inside a directory literally named `fn`, `impl`, `self`, `super`, etc. is_keyword(name) at cargo_new.rs:209 returns true.

Common situations: Creating a crate for a tutorial about a language feature and naively naming it after the keyword; directory created by a framework generator that used a keyword; copy of an example whose name happens to be reserved.

Related errors


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