{"record":{"id":"975b7f4dce496d86","repo":"ultraworkers/claw-code","slug":"unable-to-derive-an-installable-invocation-name-fr","errorCode":null,"errorMessage":"unable to derive an installable invocation name from '{}'","messagePattern":"unable to derive an installable invocation name from '(.+?)'","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"rust/crates/commands/src/lib.rs","lineNumber":3912,"sourceCode":"        std::io::ErrorKind::InvalidInput,\n        format!(\n            \"skill source '{}' must be a directory with SKILL.md or a markdown file\",\n            source.display()\n        ),\n    ))\n}\n\nfn derive_skill_install_name(\n    source: &SkillInstallSource,\n    declared_name: Option<&str>,\n) -> std::io::Result<String> {\n    for candidate in [declared_name, source.fallback_name().as_deref()] {\n        if let Some(candidate) = candidate.and_then(sanitize_skill_invocation_name) {\n            return Ok(candidate);\n        }\n    }\n\n    Err(std::io::Error::new(\n        std::io::ErrorKind::InvalidInput,\n        format!(\n            \"unable to derive an installable invocation name from '{}'\",\n            source.report_path().display()\n        ),\n    ))\n}\n\nfn sanitize_skill_invocation_name(candidate: &str) -> Option<String> {\n    let trimmed = candidate\n        .trim()\n        .trim_start_matches('/')\n        .trim_start_matches('$');\n    if trimmed.is_empty() {\n        return None;\n    }\n\n    let mut sanitized = String::new();","sourceCodeStart":3894,"sourceCodeEnd":3930,"githubUrl":"https://github.com/ultraworkers/claw-code/blob/08106b0c3771ef5b4a5aa176acccd460e88b7325/rust/crates/commands/src/lib.rs#L3894-L3930","documentation":"`derive_skill_install_name` (commands/src/lib.rs:3912) tries two candidates in order: the skill's frontmatter name, then the fallback (directory name, or file stem for a bare .md file). Each is run through `sanitize_skill_invocation_name`, which lowercases, keeps only ASCII alphanumerics and `-_.`, converts whitespace and `/`/`\\` to `-`, and trims leading/trailing separators. If BOTH candidates sanitize to an empty string, this InvalidInput error is raised.","triggerScenarios":"A skill directory named entirely in a non-Latin script (e.g. `调试技能` — every CJK char is dropped because only `is_ascii_alphanumeric` is kept); a name made solely of punctuation/emoji (`!!!`, `#1`); an empty frontmatter `name:` plus a punctuated directory name; a file stem like `---`.","commonSituations":"Skills authored by non-English teams whose directory name is the native-language skill name; markdown files named with decorative emoji prefixes; frontmatter missing a `name` field combined with a symbolic directory name.","solutions":["Add or fix a `name:` field in the SKILL.md frontmatter using at least one ASCII letter or digit.","Rename the source directory (or .md file) so its stem contains ASCII alphanumerics, e.g. `debug-skill`.","Pre-check the sanitized result yourself: lowercase, drop non-[a-z0-9-_.] chars, and confirm something survives."],"exampleFix":"# before: dir '调试技能/SKILL.md' with no frontmatter name\n/skills install ./调试技能   # unable to derive an installable invocation name from '...'\n\n# after: SKILL.md frontmatter\n---\nname: debug-skill\n---\n/skills install ./调试技能   # installs as 'debug-skill'","handlingStrategy":"validation","validationCode":"fn sanitize_preview(name: &str) -> String {\n    name.trim().trim_start_matches(['/','$']).to_ascii_lowercase()\n        .chars().map(|c| if c.is_ascii_alphanumeric() || matches!(c,'-'|'_'|'.') { c } else if c.is_whitespace() || matches!(c,'/'|'\\\\') { '-' } else { '\\0' })\n        .filter(|&c| c != '\\0').collect()  // approximate; empty result => will fail\n}\n// require a non-empty result containing an ascii alphanumeric before install","typeGuard":null,"tryCatchPattern":"if let Err(e) = install_skill(src, cwd) {\n    if e.to_string().contains(\"unable to derive an installable invocation name\") {\n        // set frontmatter `name:` with ascii letters, or rename the dir/file\n    }\n}","preventionTips":["Give every SKILL.md a frontmatter name with at least one ASCII letter/digit","Avoid directory names that are only punctuation, emoji, or non-Latin script","Non-ASCII letters are dropped (not transliterated) by the sanitizer"],"tags":["skills","install","name-sanitization","invalid-input"],"backgroundTag":"invalid-identifier","analyzedSha":"08106b0c3771ef5b4a5aa176acccd460e88b7325","analyzedAt":"2026-08-18T00:29:38.590Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}