{"record":{"id":"b993fd312c0771b6","repo":"astral-sh/uv","slug":"extra-names-must-start-and-end-with-a-letter-or-di","errorCode":null,"errorMessage":"Extra names must start and end with a letter or digit and may only contain -, _, ., and alphanumeric characters","messagePattern":"Extra names must start and end with a letter or digit and may only contain -, _, \\., and alphanumeric characters","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/uv-cli/src/lib.rs","lineNumber":117,"sourceCode":"    /// Display the dependency graph as JSON.\n    Json,\n}\n\n#[derive(Debug, Default, Clone, clap::ValueEnum)]\npub enum ListFormat {\n    /// Display the list of packages in a human-readable table.\n    #[default]\n    Columns,\n    /// Display the list of packages in a `pip freeze`-like format, with one package per line\n    /// alongside its version.\n    Freeze,\n    /// Display the list of packages in a machine-readable JSON format.\n    Json,\n}\n\nfn extra_name_with_clap_error(arg: &str) -> Result<ExtraName> {\n    ExtraName::from_str(arg).map_err(|_err| {\n        anyhow!(\n            \"Extra names must start and end with a letter or digit and may only \\\n            contain -, _, ., and alphanumeric characters\"\n        )\n    })\n}\n\n// Configures Clap v3-style help menu colors\nconst STYLES: Styles = Styles::styled()\n    .header(AnsiColor::Green.on_default().effects(Effects::BOLD))\n    .usage(AnsiColor::Green.on_default().effects(Effects::BOLD))\n    .literal(AnsiColor::Cyan.on_default().effects(Effects::BOLD))\n    .placeholder(AnsiColor::Cyan.on_default());\n\n#[derive(Parser)]\n#[command(name = \"uv\", author, long_version = crate::version::uv_self_version())]\n#[command(about = \"An extremely fast Python package manager.\")]\n#[command(\n    after_help = \"Use `uv help` for more details.\",","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/astral-sh/uv/blob/f1a42680ff5272232d65748acf338b19778dde24/crates/uv-cli/src/lib.rs#L99-L135","documentation":"`uv`'s `--extra` flags (uv pip/sync/add/tool/install etc., all wired through `extra_name_with_clap_error` at lib.rs:117) parse each comma-separated value with `ExtraName::from_str`. Extra names follow packaging normalization rules: they must begin and end with a letter or digit and may contain only `-`, `_`, `.`, and alphanumerics in between. On parse failure the clap value parser converts the error into this anyhow message.","triggerScenarios":"Passing a malformed extra: `--extra \"dev tools\"` (space), `--extra 'dev!'`, `--extra -dev` (leading dash), `--extra dev.` (trailing dot), or an empty segment from a stray comma like `--extra dev,` (the value_delimiter ',' splits it into `\"\"`).","commonSituations":"Unquoted shell arguments that split or glob; extras copied from pyproject keys that were never valid; comma lists with trailing separators; scripts building `--extra` values from unvalidated user input.","solutions":["Fix the name to match the pattern (letters/digits with `-._` allowed inside): `--extra dev --extra docs` or `--extra dev,docs`.","Quote the argument in shell so spaces/special characters cannot split it: `--extra \"dev-tools\"`.","Check the keys under `[project.optional-dependencies]` / `[tool.uv]` and use exactly those names."],"exampleFix":"# before\nuv sync --extra \"dev tools\"\n\n# after\nuv sync --extra dev --extra tools","handlingStrategy":"validation","validationCode":"import re\n\nEXTRA_RE = re.compile(r\"^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$\")\n\ndef valid_extra(name: str) -> bool:\n    return bool(EXTRA_RE.match(name)) and \"\" not in name.split(\",\")\n\n# use before invoking uv\nextras = [e for e in \"dev,docs\".split(\",\") if e]\nassert all(valid_extra(e) for e in extras), f\"invalid extra name in {extras}\"","typeGuard":"EXTRA_RE = re.compile(r\"^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$\")\n\ndef is_valid_extra_name(name: str) -> bool:\n    \"\"\"Narrow an extra name to uv's ExtraName rules: alnum start/end, -._ inside.\"\"\"\n    return isinstance(name, str) and bool(EXTRA_RE.match(name))","tryCatchPattern":null,"preventionTips":["Quote `--extra` values in shell to prevent word-splitting and glob expansion.","Validate extras against pyproject `[project.optional-dependencies]` keys before building CLI commands.","When concatenating comma lists, filter empty segments (trailing commas produce invalid empty names)."],"tags":["cli","validation","extras","packaging","arguments"],"backgroundTag":null,"analyzedSha":"f1a42680ff5272232d65748acf338b19778dde24","analyzedAt":"2026-08-16T04:51:47.599Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}