gastownhall/beads · error
unable to parse plugin file v2: %w
Error message
unable to parse plugin file v2: %w
What it means
When installed_plugins.json declares version 2 format (GH#741), GetClaudePluginVersion unmarshals it into the v2 shape whose plugins field is a map of arrays. If the content is valid JSON but does not match that v2 structure, this error is returned.
Source
Thrown at cmd/bd/doctor/claude.go:570
// First, determine the format version
var versionCheck struct {
Version int `json:"version"`
}
if err := json.Unmarshal(data, &versionCheck); err != nil {
return "", false, fmt.Errorf("unable to parse plugin file: %w", err)
}
// Handle version 2 format (GH#741): plugins map contains arrays
if versionCheck.Version == 2 {
var pluginDataV2 struct {
Plugins map[string][]struct {
Version string `json:"version"`
Scope string `json:"scope"`
} `json:"plugins"`
}
if err := json.Unmarshal(data, &pluginDataV2); err != nil {
return "", false, fmt.Errorf("unable to parse plugin file v2: %w", err)
}
// Look for beads plugin - take first entry from the array
if entries, ok := pluginDataV2.Plugins["beads@beads-marketplace"]; ok && len(entries) > 0 {
return entries[0].Version, true, nil
}
return "", false, nil
}
// Handle version 1 format (original): plugins map contains structs directly
var pluginDataV1 struct {
Plugins map[string]struct {
Version string `json:"version"`
} `json:"plugins"`
}
if err := json.Unmarshal(data, &pluginDataV1); err != nil {
return "", false, fmt.Errorf("unable to parse plugin file: %w", err)View on GitHub (pinned to 71377f2769)
Solutions
- Check the actual structure of installed_plugins.json against the v2 schema (plugins: {name: [{version, scope}]}) and fix it
- Reinstall the beads plugin so Claude regenerates a schema-correct manifest
- Update bd to the latest version in case newer manifest variants are supported
- If a newer Claude wrote an incompatible format, report it to beads maintainers with the file contents (redact paths as needed)
Example fix
// before (invalid v2 shape)
{"version":2,"plugins":["beads@beads-marketplace"]}
// after
{"version":2,"plugins":{"beads@beads-marketplace":[{"version":"0.9.1","scope":"user"}]}} Defensive patterns
Strategy: type-guard
Validate before calling
import "encoding/json", "os"
func isV2Shape(home string) bool {
data, _ := os.ReadFile(filepath.Join(home, ".claude", "plugins", "installed_plugins.json"))
var m struct {
Version int `json:"version"`
Plugins map[string][]struct{ Version string `json:"version"` } `json:"plugins"`
}
return json.Unmarshal(data, &m) == nil && m.Version == 2
} Try / catch
_, installed, err := GetClaudePluginVersion()
if err != nil && strings.Contains(err.Error(), "unable to parse plugin file v2") {
log.Printf("manifest claims v2 but has wrong shape: %v", err)
return nil // handle reinstall path
} Prevention
- Keep plugins maps as objects of arrays in v2 manifests ({name: [entries]})
- Don't mix v1 and v2 shapes when editing manifests manually
- Regenerate the manifest by reinstalling rather than hand-editing
- Keep bd and the Claude plugin updated together (GH#741 format change)
When it happens
Trigger: GetClaudePluginVersion (via CheckClaudePlugin) when versionCheck.Version == 2 and json.Unmarshal into the v2 struct fails — e.g. "plugins" is an array instead of an object, or entries are objects instead of arrays — at cmd/bd/doctor/claude.go:570.
Common situations: A Claude plugin tool changed the manifest layout while keeping version: 2; manually edited plugins map with wrong nesting; forward-compatible schema drift from a newer Claude version.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unable to parse plugin file: %w
- failed to write JSON: %w
- unable to determine home directory: %w
- unable to read plugin file: %w
- failed to parse issue from JSONL: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/7833c3555b2283c8.
Report an issue: GitHub.