{"record":{"id":"33a958e342d6098e","repo":"larksuite/cli","slug":"s-q-is-not-a-valid-skill-name","errorCode":null,"errorMessage":"%s: %q is not a valid skill name","messagePattern":"(.+?): %q is not a valid skill name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillpolicy/resolver.go","lineNumber":181,"sourceCode":"// scanSkillTree validates and snapshots a skill tree's top level in one\n// pass. The returned set is the only source used by validation and overlay\n// composition, so a mutable FS cannot swap unvalidated names between phases.\nfunc scanSkillTree(label string, source fs.FS) (skillTreeSnapshot, error) {\n\tsnapshot := skillTreeSnapshot{source: source, skills: map[string]skillManifest{}}\n\tif source == nil {\n\t\treturn snapshot, nil\n\t}\n\tentries, err := fs.ReadDir(source, \".\")\n\tif err != nil {\n\t\treturn snapshot, fmt.Errorf(\"%s: cannot read root: %w\", label, err)\n\t}\n\tfor _, e := range entries {\n\t\tname := e.Name()\n\t\tif !e.IsDir() {\n\t\t\treturn snapshot, fmt.Errorf(\"%s: %q is not a directory; every %s entry must be a <skill>/ dir\", label, name, label)\n\t\t}\n\t\tif !isSkillName(name) {\n\t\t\treturn snapshot, fmt.Errorf(\"%s: %q is not a valid skill name\", label, name)\n\t\t}\n\t\tok, err := skillExists(source, name)\n\t\tif err != nil {\n\t\t\treturn snapshot, fmt.Errorf(\"%s: probing skill %q: %w\", label, name, err)\n\t\t}\n\t\tif !ok {\n\t\t\treturn snapshot, fmt.Errorf(\"%s: skill %q is missing SKILL.md\", label, name)\n\t\t}\n\t\tmanifest, err := readSkillManifest(source, name)\n\t\tif err != nil {\n\t\t\treturn snapshot, fmt.Errorf(\"%s: skill %q has invalid metadata: %w\", label, name, err)\n\t\t}\n\t\tsnapshot.skills[name] = manifest\n\t}\n\treturn snapshot, nil\n}\n\n// validateSelection rejects allow/remove entries that cannot compose against","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillpolicy/resolver.go#L163-L199","documentation":"A top-level directory name fails skillref.ValidSkillName (isSkillName): empty, dotted, or otherwise invalid as a skill identifier. Skill directory names are part of the reference contract, so malformed names are rejected at composition time.","triggerScenarios":"ResolveWithReferences over a Base/Overlay FS containing a directory whose name is not a valid skill name (e.g. leading dots, slashes cannot occur at top level, empty or reserved characters).","commonSituations":"Directories like '.github', '__pycache__', 'my skill' with spaces, or a renamed skill directory using PascalCase when the contract expects a specific naming convention.","solutions":["Rename the offending directory (named in the message) to a valid skill name per skillref.ValidSkillName","Exclude non-skill hidden/system directories from the embedded tree","Pre-validate candidate names with skillref.ValidSkillName before adding a skill","Check for accidental dot-directories swept into an embed pattern"],"exampleFix":"// before\noverlay/.github/  // invalid entry\n// after\noverlay/deploy-guide/ // valid skill name; move .github out of the tree","handlingStrategy":"validation","validationCode":"for _, name := range dirNames {\n\tif !skillref.ValidSkillName(name) { return fmt.Errorf(\"invalid skill name %q\", name) }\n}","typeGuard":null,"tryCatchPattern":"_, err := skillpolicy.ResolveWithReferences(base, specs)\nif err != nil && strings.Contains(err.Error(), \"is not a valid skill name\") { /* rename dir */ }","preventionTips":["Validate directory names with skillref.ValidSkillName before adding skills","Exclude hidden/system dirs from embeds","Use lowercase-kebab skill names consistently"],"tags":["go","naming","validation","skill-tree"],"backgroundTag":"invalid-skill-name","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}