{"record":{"id":"846142392c6d3e43","repo":"larksuite/cli","slug":"invalid-skill-name-q","errorCode":null,"errorMessage":"invalid skill name %q","messagePattern":"invalid skill name %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillscheck/sync.go","lineNumber":114,"sourceCode":"\t\tName   string `json:\"name\"`\n\t\tType   string `json:\"type\"`\n\t\tURL    string `json:\"url\"`\n\t\tDigest string `json:\"digest\"`\n\t}\n\ttype officialIndex struct {\n\t\tSkills []officialSkill `json:\"skills\"`\n\t}\n\n\tvar index officialIndex\n\tif err := json.Unmarshal([]byte(text), &index); err != nil {\n\t\treturn nil, err\n\t}\n\n\tseen := map[string]bool{}\n\tfor _, skill := range index.Skills {\n\t\tcandidate := strings.TrimSpace(skill.Name)\n\t\tif !skillNamePattern.MatchString(candidate) {\n\t\t\treturn nil, fmt.Errorf(\"invalid skill name %q\", candidate)\n\t\t}\n\t\tif skill.Type != \"archive\" || strings.TrimSpace(skill.URL) == \"\" || !digestPattern.MatchString(skill.Digest) {\n\t\t\treturn nil, fmt.Errorf(\"skill %s is not a complete v0.2 archive entry\", candidate)\n\t\t}\n\t\tif seen[candidate] {\n\t\t\treturn nil, fmt.Errorf(\"duplicate skill %s\", candidate)\n\t\t}\n\t\tseen[candidate] = true\n\t}\n\n\treturn sortedKeys(seen), nil\n}\n\n// parseGlobalSkillsList parses the output of \"npx -y skills ls -g\"\nfunc parseGlobalSkillsList(lines []string) []string {\n\tseen := map[string]bool{}\n\n\tfor _, line := range lines {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillscheck/sync.go#L96-L132","documentation":"ParseOfficialSkillsIndexJSON rejects an index entry whose name (after trimming) does not match skillNamePattern. This enforces a strict naming convention for skills fetched from the official index so downstream filesystem paths and references stay safe.","triggerScenarios":"fetchOfficialSkills parsing an official skills index JSON where an entry's `name` field contains characters outside the allowed pattern (spaces, slashes, uppercase/odd punctuation) or is empty after trimming.","commonSituations":"A typo or format change in the upstream index file; a mirror or proxy serving a modified/older index; hand-crafted test or custom index files that don't follow the v0.2 naming rules.","solutions":["Fix the `name` field in the index entry to conform to the skillNamePattern (typically lowercase-kebab identifiers)","Check whether the upstream index schema changed and update the parser or pin to a supported index version","If serving a custom index, validate all skill names against the pattern before publishing","Inspect the raw index JSON to identify the offending entry at the reported name"],"exampleFix":"// before\n{\"name\": \"My Skill/Backup\", \"type\": \"archive\", ...}\n// after\n{\"name\": \"my-skill-backup\", \"type\": \"archive\", ...}","handlingStrategy":"validation","validationCode":"var idx struct{ Skills []struct{ Name string `json:\"name\"` } `json:\"skills\"` }\nif err := json.Unmarshal(indexData, &idx); err == nil {\n    for _, s := range idx.Skills {\n        name := strings.TrimSpace(s.Name)\n        if name == \"\" || strings.ContainsAny(name, \" /\\\\\") || strings.ToLower(name) != name {\n            // reject or fix before calling fetchOfficialSkills\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"skills, err := skillscheck.ParseOfficialSkillsIndexJSON(data)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid skill name\") {\n        // fetch and log the raw index for inspection, then retry with corrected index\n    }\n    return err\n}","preventionTips":["Keep skill names lowercase-kebab-case in any custom index","Validate index entries against the naming pattern before publishing","Pin to the official index URL rather than mirrors","Re-run parsing tests against upstream index changes"],"tags":["validation","json","skills-index"],"backgroundTag":"invalid-input-format","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"}