{"record":{"id":"e3655a77301c0c32","repo":"Tencent/WeKnora","slug":"this-tool-is-not-bound-to-a-skill-directory","errorCode":null,"errorMessage":"this tool is not bound to a skill directory","messagePattern":"this tool is not bound to a skill directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/skill_file.go","lineNumber":391,"sourceCode":"}\n\n// Cleanup releases any resources.\nfunc (t *EditSkillFileTool) Cleanup(ctx context.Context) error {\n\treturn nil\n}\n\n// resolveSkillFilePath turns a model-supplied path into an absolute path\n// proven to sit inside skillDir.\n//\n// A relative path is resolved against skillDir, which is what the model\n// reaches for after being told the directory once. Everything is then cleaned\n// and re-checked against the prefix, so \"..\", a symlink-looking spelling or an\n// absolute path into a neighbouring skill all fail here rather than reaching\n// the image. The directory itself is refused: it is not a file.\nfunc resolveSkillFilePath(skillDir, requested string) (string, error) {\n\tdir := path.Clean(strings.TrimSpace(skillDir))\n\tif dir == \"\" || dir == \".\" || dir == \"/\" {\n\t\treturn \"\", fmt.Errorf(\"this tool is not bound to a skill directory\")\n\t}\n\ttrimmed := strings.TrimSpace(requested)\n\tif trimmed == \"\" {\n\t\treturn \"\", fmt.Errorf(\"path is required; write a file inside %s\", dir)\n\t}\n\tif strings.ContainsRune(trimmed, 0) {\n\t\treturn \"\", fmt.Errorf(\"path %q is not a valid file path\", requested)\n\t}\n\tcandidate := trimmed\n\tif !path.IsAbs(candidate) {\n\t\tcandidate = path.Join(dir, candidate)\n\t}\n\tclean := path.Clean(candidate)\n\tif clean == dir || !strings.HasPrefix(clean, dir+\"/\") {\n\t\treturn \"\", fmt.Errorf(\n\t\t\t\"path %q is outside this install's skill directory (%s); \"+\n\t\t\t\t\"an install may only write its own skill\",\n\t\t\trequested, dir,","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/agent/tools/skill_file.go#L373-L409","documentation":"resolveSkillFilePath rejects a skillDir that is empty, \".\", or \"/\" after Clean+TrimSpace, because the skill-file tool must be bound to a concrete skill installation directory to safely scope file writes. Returning this error means the tool instance was constructed without a valid skill directory binding, not that the requested path itself is bad.","triggerScenarios":"Calling Execute on a skill-file tool whose skillDir binding is empty/missing, or explicitly \".\" or \"/\" — e.g. the tool was created before the skill install directory was resolved.","commonSituations":"Tool constructed with a zero-value config; skill installation lookup failed earlier and an empty dir was propagated; tests instantiating the tool without a fixture directory.","solutions":["Construct/configure the tool with the concrete installed skill directory path.","Fail earlier at setup if the skill directory cannot be resolved instead of registering the tool.","In tests, point the tool at a temporary fixture skill directory."],"exampleFix":"// before\ntool := tools.NewSkillFileTool(\"\") // unbound\n// after\nskillDir := filepath.Join(cfg.SkillsRoot, install.Name)\nif skillDir == \"\" {\n    return errors.New(\"cannot register skill file tool: no install dir\")\n}\ntool := tools.NewSkillFileTool(skillDir)","handlingStrategy":"type-guard","validationCode":"d := path.Clean(strings.TrimSpace(skillDir))\nif d == \"\" || d == \".\" || d == \"/\" {\n    return errors.New(\"skill file tool requires a concrete skill directory\")\n}","typeGuard":"func isBoundToSkillDir(dir string) bool {\n    d := path.Clean(strings.TrimSpace(dir))\n    return d != \"\" && d != \".\" && d != \"/\"\n}","tryCatchPattern":"out, err := tool.Execute(ctx, input)\nif err != nil && strings.Contains(err.Error(), \"not bound to a skill directory\") {\n    // resolve the install dir, rebind the tool, then retry once\n}","preventionTips":["Resolve and verify the skill install directory before constructing the tool","Skip tool registration when the skill directory cannot be resolved","Use real fixture directories in tests instead of empty strings"],"tags":["configuration","path-safety","skill"],"backgroundTag":"missing-dependency-not-configured","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}