siyuan-note/siyuan · error
invalid skill resource path: %s
Error message
invalid skill resource path: %s
What it means
normalizeSkillResourcePath sanitizes the resource portion of a skill locator before it is joined to the skill directory. It rejects absolute paths, parent traversals ('..'), Windows drive/volume prefixes, and backslash-based escape attempts, returning this error for anything unsafe.
Source
Thrown at kernel/util/skill.go:289
}
result.Content = content
result.ResourcePath = resource
return result, nil
}
func splitSkillLocator(locator string) (name, resource string) {
locator = strings.TrimSpace(strings.ReplaceAll(locator, `\`, "/"))
name, resource, _ = strings.Cut(locator, "/")
return
}
func normalizeSkillResourcePath(resource string) (string, error) {
resource = strings.ReplaceAll(resource, `\`, "/")
cleaned := path.Clean(resource)
native := filepath.FromSlash(cleaned)
if cleaned == "." || path.IsAbs(cleaned) || cleaned == ".." || strings.HasPrefix(cleaned, "../") ||
filepath.IsAbs(native) || filepath.VolumeName(native) != "" || hasWindowsDrivePrefix(cleaned) {
return "", fmt.Errorf("invalid skill resource path: %s", resource)
}
return cleaned, nil
}
func hasWindowsDrivePrefix(resource string) bool {
if len(resource) < 2 || resource[1] != ':' {
return false
}
drive := resource[0]
return 'a' <= drive && drive <= 'z' || 'A' <= drive && drive <= 'Z'
}
func listSkillResources(skillDir string) (resources []string, truncated bool) {
realRoot, err := filepath.EvalSymlinks(skillDir)
if err != nil {
return nil, false
}
View on GitHub (pinned to 8641553a1f)
Solutions
- Use a relative path within the skill directory (e.g. 'scripts/helper.md')
- Strip leading '/' and any '..' segments from the resource path before calling
- Convert backslashes to forward slashes and remove drive letters
- Reference the file via the skill's declared bundled resources list
Example fix
// before
LoadSkill("my-skill/../../../etc/passwd", enabled)
// after
LoadSkill("my-skill/references/usage.md", enabled) Defensive patterns
Strategy: validation
Validate before calling
function isSafeResourcePath(p) {
const c = p.replace(/\\/g, "/");
return !c.startsWith("/") && !/^[A-Za-z]:/.test(c) &&
c !== "." && !c.split("/").includes("..");
}
if (!isSafeResourcePath(resource)) sanitizeBeforeCall(resource); Try / catch
try {
return await loadSkillResource(locator);
} catch (e) {
if (String(e).includes("invalid skill resource path")) {
return resolveInsideSkillOnly(locator); // rewrite the path relative to the skill
}
throw e;
} Prevention
- Always build resource locators from skill-relative relative paths
- Strip drive letters, backslashes, and '..' segments from any user/LLM-supplied path
- Never construct locators from unvalidated external input
When it happens
Trigger: Calling LoadSkill with a locator whose resource part is absolute ('/etc/passwd', 'C:\x'), contains '..' segments, has a Windows drive prefix, or resolves to '.'/'..' after path.Clean.
Common situations: LLM-generated resource paths containing traversal sequences; copying Windows-style paths with backslashes or drive letters into the locator; attempting to read files outside the skill directory on purpose.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- invalid custom emoji name
- path [%s] must not contain '..'
- path [%s] escapes box directory
- asset path is outside data directory
- invalid package name
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/d6b6a82fb945d08b.
Report an issue: GitHub.