{"record":{"id":"3d135b6afc11770b","repo":"larksuite/cli","slug":"local-skills-list-failed","errorCode":null,"errorMessage":"local skills list failed","messagePattern":"local skills list failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillscheck/sync.go","lineNumber":383,"sourceCode":"\tjsonResult := runner.ListGlobalSkillsJSON()\n\tif jsonResult != nil && jsonResult.Err == nil {\n\t\tif installed, err := parseInstalledSkillsJSON(jsonResult.Stdout.String()); err == nil {\n\t\t\treturn installed, nil\n\t\t}\n\t}\n\n\ttextResult := runner.ListGlobalSkills()\n\tif textResult != nil && textResult.Err == nil {\n\t\tnames := ParseSkillsList(textResult.Stdout.String())\n\t\tif names != nil {\n\t\t\tinstalled := make([]installedSkill, 0, len(names))\n\t\t\tfor _, name := range names {\n\t\t\t\tinstalled = append(installed, installedSkill{Name: name})\n\t\t\t}\n\t\t\treturn installed, nil\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"local skills list failed\")\n}\n\nfunc localOfficialSkills(installed []installedSkill, previous *SkillsState, readable bool) ([]string, error) {\n\tif !readable || previous == nil || EffectiveLayout(previous) == LayoutSeparate {\n\t\tnames := make([]string, 0, len(installed))\n\t\tfor _, skill := range installed {\n\t\t\tnames = append(names, skill.Name)\n\t\t}\n\t\treturn names, nil\n\t}\n\n\tfor _, skill := range installed {\n\t\tif skill.Name != \"lark-suite\" {\n\t\t\tcontinue\n\t\t}\n\t\tif skill.Path == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"cannot inspect installed lark-suite: global skills JSON did not include its path\")\n\t\t}","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillscheck/sync.go#L365-L401","documentation":"listInstalledSkills tries two local discovery paths: the runner's global skills JSON listing (parseInstalledSkillsJSON) and its plain-text listing (ParseSkillsList). If both fail or yield unparseable output, it returns this fixed-message error and SyncSkills aborts with Action=\"failed\". It means the CLI cannot determine which skills are installed locally, so a safe sync plan cannot be computed.","triggerScenarios":"SyncSkills -> listInstalledSkills where ListGlobalSkillsJSON returns nil/Err or unparseable JSON AND ListGlobalSkills returns nil/Err or unparseable text output.","commonSituations":"The skills CLI binary is missing, broken, or an incompatible version; the local skills directory was deleted or has bad permissions; the JSON output format changed between CLI versions so the parser rejects it; a corrupted local state file; running in a sandbox where the CLI cannot execute.","solutions":["Run the underlying skills list command manually and check its stdout/stderr for the real failure","Reinstall or update the skills CLI so its JSON output matches the expected schema","Fix permissions/existence of the local skills installation directory","Clear or repair corrupted local skills state, then retry the sync","Check CLI version compatibility — a schema change in list output breaks both parse paths"],"exampleFix":"// before: both parse paths fail silently\ninstalled, err := listInstalledSkills(runner) // err: local skills list failed\n// after: capture raw outputs for diagnosis\njsonResult := runner.ListGlobalSkillsJSON()\nif jsonResult == nil || jsonResult.Err != nil {\n    log.Printf(\"JSON listing failed: %v (stdout=%q)\", errString(jsonResult), rawOf(jsonResult))\n}\ninstalled, err := listInstalledSkills(runner)","handlingStrategy":"type-guard","validationCode":"if jr := runner.ListGlobalSkillsJSON(); jr != nil && jr.Err == nil {\n    if _, err := parseInstalledSkillsJSON(jr.Stdout.String()); err != nil {\n        log.Printf(\"local JSON listing unparseable, falling back to text listing\")\n    }\n}","typeGuard":"func localSkillsReadable(runner SkillsRunner) bool {\n    if jr := runner.ListGlobalSkillsJSON(); jr != nil && jr.Err == nil {\n        if _, err := parseInstalledSkillsJSON(jr.Stdout.String()); err == nil {\n            return true\n        }\n    }\n    if tr := runner.ListGlobalSkills(); tr != nil && tr.Err == nil {\n        return ParseSkillsList(tr.Stdout.String()) != nil\n    }\n    return false\n}","tryCatchPattern":"if !localSkillsReadable(runner) {\n    // run the list command manually, capture stderr, fix CLI/version before syncing\n    out, execErr := exec.Command(\"lark-cli\", \"skills\", \"list\", \"--json\").CombinedOutput()\n    return fmt.Errorf(\"local skills unreadable: %v (out=%s)\", execErr, out)\n}\ninstalled, err := listInstalledSkills(runner)\nif err != nil { return err }","preventionTips":["Run the skills list command manually after upgrading the CLI to confirm output schema compatibility","Keep the local skills directory present with correct read permissions","Do not mutate the skills installation directory while a sync is in flight","Test listInstalledSkills against a stub runner in CI to catch parser/CLI drift early"],"tags":["go","local-state","skills-cli","parsing"],"backgroundTag":"local-skills-list-failed","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"}