Hmbown/CodeWhale · error

skill name must be a single path-safe segment (got '{name}')

Error message

skill name must be a single path-safe segment (got '{name}')

What it means

First clause of validate_skill_name_segment: a skill name must be non-empty, equal to its own trim (no leading or trailing whitespace), and contain no whitespace characters at all. The check runs before any path join, so install/uninstall/update targeting refuses whitespace-bearing names outright.

Source

Thrown at crates/tui/src/skills/install.rs:1546

    }
    for component in path.components() {
        match component {
            Component::ParentDir => return false,
            Component::Prefix(_) | Component::RootDir => return false,
            _ => {}
        }
    }
    true
}

fn skill_target_path(name: &str, skills_dir: &Path) -> Result<PathBuf> {
    let name = validate_skill_name_segment(name)?;
    Ok(skills_dir.join(name))
}

pub(crate) fn validate_skill_name_segment(name: &str) -> Result<&str> {
    if name.is_empty() || name.trim() != name || name.chars().any(char::is_whitespace) {
        bail!("skill name must be a single path-safe segment (got '{name}')");
    }
    if name == "." || name == ".." || name.contains('/') || name.contains('\\') {
        bail!("skill name must be a single path-safe segment (got '{name}')");
    }
    let mut components = Path::new(name).components();
    if !matches!(components.next(), Some(Component::Normal(_))) || components.next().is_some() {
        bail!("skill name must be a single path-safe segment (got '{name}')");
    }
    Ok(name)
}

fn ensure_target_within_skills_dir(target: &Path, skills_dir: &Path) -> Result<()> {
    let skills_dir = fs::canonicalize(skills_dir)
        .with_context(|| format!("failed to resolve {}", skills_dir.display()))?;
    let target = fs::canonicalize(target)
        .with_context(|| format!("failed to resolve {}", target.display()))?;
    if !target.starts_with(&skills_dir) {
        bail!(

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Strip whitespace and quote the argument when invoking skill commands.
  2. Use hyphens instead of spaces in skill names (the ecosystem convention).
  3. Validate names in wrappers with the same rule before calling the API.

Example fix

# before
/skill uninstall "my skill"

# after
/skill uninstall my-skill
Defensive patterns

Strategy: validation

Validate before calling

fn name_has_no_whitespace(name: &str) -> bool {
    !name.is_empty() && name.trim() == name && !name.chars().any(char::is_whitespace)
}

Type guard

fn is_valid_skill_name(name: &str) -> bool {
    !name.is_empty()
        && name.trim() == name
        && !name.chars().any(char::is_whitespace)
        && name != "." && name != ".."
        && !name.contains('/') && !name.contains('\\')
        && {
            let mut c = std::path::Path::new(name).components();
            matches!(c.next(), Some(std::path::Component::Normal(_))) && c.next().is_none()
        }
}

Prevention

When it happens

Trigger: Passing a skill name like ' my-skill', 'my skill', or '' to install/uninstall/update; typically from shell word-splitting or quoted arguments containing stray spaces.

Common situations: Copy-paste with a trailing newline or space, names derived from display labels containing spaces, and scripts passing unquoted empty variables.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/3136fc2035349d8b. Report an issue: GitHub.