Hmbown/CodeWhale · error

skill path {} escapes skills directory {}

Error message

skill path {} escapes skills directory {}

What it means

Guard in ensure_target_within_skills_dir fired because the computed install target for the skill resolves to a path outside the configured skills directory. The skill name (or archive layout) produced a target containing traversal or an absolute component, so installing it would write outside CodeWhale-owned storage; this is a path-traversal rejection, not a filesystem failure.

Source

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

        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!(
            "skill path {} escapes skills directory {}",
            target.display(),
            skills_dir.display()
        );
    }
    Ok(())
}

/// Strip a leading directory prefix (e.g. `repo-main/`) from a tarball path.
fn strip_prefix<'a>(path: &'a str, prefix: &str) -> std::borrow::Cow<'a, str> {
    if prefix.is_empty() {
        return std::borrow::Cow::Borrowed(path);
    }
    let with_slash = format!("{prefix}/");
    if let Some(rest) = path.strip_prefix(&with_slash) {
        std::borrow::Cow::Owned(rest.to_string())
    } else if path == prefix {
        std::borrow::Cow::Borrowed("")

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Reject the skill source; its declared name or layout produces an unsafe target path
  2. Ask the skill author to rename the skill to a single path-safe segment
  3. If installing from an archive, verify the top-level directory name contains no '..' or separators
  4. Install to the correct skills directory so the resolved target stays inside it
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/tui/src/skills/install.rs:1564 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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