{"record":{"id":"59f53a40bd909f82","repo":"cli/cli","slug":"s-is-not-a-directory","errorCode":null,"errorMessage":"%s is not a directory","messagePattern":"(.+?) is not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skills/discovery/discovery.go","lineNumber":985,"sourceCode":"\t\t)\n\t}\n\treturn skills, nil\n}\n\n// DiscoverLocalSkillsWithOptions finds skills in a local directory using the\n// same conventions as remote discovery, with configurable discovery behavior.\nfunc DiscoverLocalSkillsWithOptions(dir string, opts DiscoverOptions) ([]Skill, error) {\n\tabsDir, err := filepath.Abs(dir)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not resolve path: %w\", err)\n\t}\n\n\tinfo, err := os.Stat(absDir)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not access %s: %w\", dir, err)\n\t}\n\tif !info.IsDir() {\n\t\treturn nil, fmt.Errorf(\"%s is not a directory\", dir)\n\t}\n\n\tif _, err := os.Stat(filepath.Join(absDir, \"SKILL.md\")); err == nil {\n\t\tskill, err := localSkillFromDir(absDir)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tskill.Path = \".\"\n\t\treturn []Skill{*skill}, nil\n\t}\n\n\tvar skills []Skill\n\tseen := make(map[string]bool)\n\n\terr = filepath.Walk(absDir, func(p string, info os.FileInfo, walkErr error) error {\n\t\tif walkErr != nil {\n\t\t\treturn walkErr\n\t\t}","sourceCodeStart":967,"sourceCodeEnd":1003,"githubUrl":"https://github.com/cli/cli/blob/0eeec0b92edbe70199f9768522f831d3534f41ad/internal/skills/discovery/discovery.go#L967-L1003","documentation":"DiscoverLocalSkillsWithOptions stats the path successfully but it is not a directory (e.g. a regular file, socket, or symlink to a file). Discovery needs to walk a directory tree, so it refuses immediately with this explicit message instead of a confusing walk error.","triggerScenarios":"Passing a path to a SKILL.md file or any regular file instead of its containing directory; passing a symlink that resolves to a file.","commonSituations":"Users assuming the API takes the manifest file path rather than a directory; shell variables that expand to a file (e.g. $SKILL_PATH set to .../skills/my-skill/SKILL.md); tab-completion landing on the file.","solutions":["Pass the directory that contains the skills, e.g. the repo root or the skills/ folder, not the SKILL.md file","If pointing at a single-skill directory, give the directory that holds SKILL.md (its parent), per the documented layouts","For symlinked paths, confirm the link target is a directory: readlink -f <path>"],"exampleFix":"# before\ndiscovery.DiscoverLocalSkills(\"repo/skills/my-skill/SKILL.md\")\n\n# after\ndiscovery.DiscoverLocalSkills(\"repo\")","handlingStrategy":"validation","validationCode":"if info, err := os.Stat(dir); err == nil && !info.IsDir() {\n    return fmt.Errorf(\"%s is a file; pass its directory (or the skills root) instead\", dir)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Document that discovery takes directories, never SKILL.md file paths","Normalize inputs: filepath.Dir for anything ending in SKILL.md before calling"],"tags":["filesystem","path","go"],"backgroundTag":null,"analyzedSha":"0eeec0b92edbe70199f9768522f831d3534f41ad","analyzedAt":"2026-08-15T12:31:05.478Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}