{"record":{"id":"286825819f462661","repo":"larksuite/cli","slug":"cannot-inspect-installed-lark-suite-global-skills","errorCode":null,"errorMessage":"cannot inspect installed lark-suite: global skills JSON did not include its path","messagePattern":"cannot inspect installed lark-suite: global skills JSON did not include its path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skillscheck/sync.go","lineNumber":400,"sourceCode":"\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}\n\t\treturn listDirectSubdirs(filepath.Join(skill.Path, \"references\"))\n\t}\n\treturn nil, fmt.Errorf(\"cannot inspect installed lark-suite: skill is not installed\")\n}\n\nfunc listDirectSubdirs(root string) ([]string, error) {\n\tentries, err := vfs.ReadDir(root)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnames := []string{}\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tnames = append(names, entry.Name())\n\t\t}\n\t}\n\tsort.Strings(names)","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/skillscheck/sync.go#L382-L418","documentation":"localOfficialSkills builds the list of locally-installed official skills. When the previous state indicates suite layout, it locates the installed 'lark-suite' entry in the global skills JSON and inspects its references directory. This error is thrown when a 'lark-suite' entry exists but its Path field is empty, meaning the listing did not include install-location metadata, so the tool cannot determine where the suite is installed to enumerate its references.","triggerScenarios":"Calling SyncSkills (via localOfficialSkills) when the previous skills state reports suite layout, the global skills listing was parsed successfully and contains a 'lark-suite' entry, but that entry has an empty Path — typically because the listing only carries skill names (e.g. derived from the text fallback in listInstalledSkills, which builds installedSkill{Name: name} without a path).","commonSituations":"The host CLI's JSON listing failed so the code fell back to the name-only text listing; an older CLI version emits JSON without path fields; the global skills JSON was hand-edited or corrupted; or the state file says suite layout while the listing format changed between CLI versions.","solutions":["Reinstall lark-suite globally so the host CLI produces a fresh listing including the install path, then re-run sync.","Clear the cached skills state so localOfficialSkills takes the name-only branch instead of requiring the path.","Upgrade the host CLI so its global skills JSON format includes path metadata for every installed skill.","Inspect the global skills JSON (`lark skills list --json` or equivalent) to confirm the lark-suite entry includes a path; if not, report or fix the CLI version emitting it."],"exampleFix":"// before: name-only installed list used in suite layout\n//   installed := [{Name: \"lark-suite\", Path: \"\"}]\n//   names, err := localOfficialSkills(installed, previous, true) // -> error\n// after: degrade gracefully when path metadata is absent\nif skill.Path == \"\" {\n    // fall back to name-only enumeration instead of failing\n    return namesFromInstalled(installed), nil\n}\nreturn listDirectSubdirs(filepath.Join(skill.Path, \"references\"))","handlingStrategy":"validation","validationCode":"installed, err := listInstalledSkills(runner)\nif err != nil { return err }\nif readable && previous != nil && EffectiveLayout(previous) != LayoutSeparate {\n    suite, ok := findSkill(installed, \"lark-suite\")\n    if !ok {\n        return fmt.Errorf(\"state says suite layout but lark-suite is not installed\")\n    }\n    if suite.Path == \"\" {\n        // listing lacks path metadata; refresh listing JSON or clear state first\n        return fmt.Errorf(\"global skills listing lacks path for lark-suite\")\n    }\n}","typeGuard":"func hasInstallPath(s installedSkill) bool { return s.Path != \"\" }\n\nfunc findSkill(installed []installedSkill, name string) (installedSkill, bool) {\n    for _, s := range installed {\n        if s.Name == name { return s, true }\n    }\n    return installedSkill{}, false\n}","tryCatchPattern":"names, err := localOfficialSkills(installed, previous, readable)\nif err != nil {\n    if strings.Contains(err.Error(), \"global skills JSON did not include its path\") {\n        // degrade to name-only enumeration and let sync rebuild the state\n        names = namesFromInstalled(installed)\n    } else {\n        return err\n    }\n}","preventionTips":["Keep the host CLI current so its global skills JSON always emits path metadata.","Refresh the cached skills state after any manual npm global install/uninstall.","Verify `lark skills list --json` output includes paths before running sync in suite layout.","Avoid hand-editing global skills JSON or the skills state file."],"tags":["skills-sync","npm","missing-path","suite-layout"],"backgroundTag":"installed-skill-path-missing","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"}