Tencent/WeKnora · error · ErrInvalidSkillName

ErrInvalidSkillName

ErrInvalidSkillName

Error message

%w %q

What it means

SkillDirFor maps a skill name (from SKILL.md) to its directory under the skills image root, but only if the name passes IsValidSkillName. This sentinel-wrapped error (errors.Is-able via ErrInvalidSkillName) is thrown for names that could escape the skills directory — empty names, path separators, "..", slashes, etc.

Source

Thrown at internal/sandbox/skill_paths.go:47

// separator are rejected so install/exec cannot walk out of the skills root.
func IsValidSkillName(name string) bool {
	name = strings.TrimSpace(name)
	if name == "" || name == "." || name == ".." {
		return false
	}
	if strings.ContainsAny(name, "/\\\x00") {
		return false
	}
	return path.Base(path.Clean(name)) == name
}

// SkillDirFor returns the image directory of a skill. The key is the skill
// name from SKILL.md: that is what the installer writes, and what the agent
// is told to execute. The database id stays a row key and is not part of
// the path.
func SkillDirFor(skillName string) (string, error) {
	if !IsValidSkillName(skillName) {
		return "", fmt.Errorf("%w %q", ErrInvalidSkillName, skillName)
	}
	return path.Join(SkillsImageRoot, skillName), nil
}

// SkillRequirementsPath is where the installer agent writes what the skill
// needs at run time. It lives inside the skill's own directory so it travels
// with the skill into the snapshot, and it is a file rather than a tool call
// on purpose: the agent reads a third-party SKILL.md, so nothing it produces
// may reach the database except as bytes the server parses and validates.
//
// An invalid skill name yields an empty path, which every caller treats as
// "no declaration": the name is already validated before an install starts,
// and a best-effort read is not a place to fail an install.
func SkillRequirementsPath(skillName string) string {
	dir, err := SkillDirFor(skillName)
	if err != nil {
		return ""
	}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Validate the name with IsValidSkillName before use and reject/repair bad values at ingestion time.
  2. Normalize the stored skill name to the allowed charset (letters/digits/dashes per the validator) and re-save it.
  3. Check errors.Is(err, sandbox.ErrInvalidSkillName) and surface a user-facing 'invalid skill name' message instead of a raw path error.

Example fix

// before
dir, err := sandbox.SkillDirFor(userInput) // may be "../x"
// after
if !sandbox.IsValidSkillName(userInput) {
    return fmt.Errorf("invalid skill name %q", userInput)
}
dir, err := sandbox.SkillDirFor(userInput)
Defensive patterns

Strategy: validation

Validate before calling

if !sandbox.IsValidSkillName(name) {
    return fmt.Errorf("invalid skill name %q", name)
}

Type guard

func validSkillName(name string) bool { return sandbox.IsValidSkillName(name) }

Try / catch

dir, err := sandbox.SkillDirFor(name)
if errors.Is(err, sandbox.ErrInvalidSkillName) {
    return fmt.Errorf("skill %q has an invalid name; fix SKILL.md", name)
}

Prevention

When it happens

Trigger: Calling SkillDirFor, or any of its callers (DiscoverSkills, LoadSkillInstructions, LoadSkillFile, GetSkillBasePath, RemoteScriptPath, relativeSkillFileFromImagePath) with a skillName failing IsValidSkillName — e.g. "", "../escape", "a/b", or names with illegal characters.

Common situations: Storing skill names from user input or a database with slashes/whitespace; older records predating the naming rule; hand-edited SKILL.md name fields; constructing skill paths from URL segments.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/dace1ec2cb177e5b. Report an issue: GitHub.