gastownhall/beads · error
unable to parse plugin file: %w
Error message
unable to parse plugin file: %w
What it means
After reading installed_plugins.json, GetClaudePluginVersion first unmarshals it to detect the format version. If the file is not valid JSON at all, this error wraps the json.Unmarshal failure, indicating the plugin manifest is malformed.
Source
Thrown at cmd/bd/doctor/claude.go:558
// Path to installed_plugins.json
pluginPath := filepath.Join(homeDir, ".claude", "plugins", "installed_plugins.json")
// Read plugin file
data, err := os.ReadFile(pluginPath) // #nosec G304 - path is controlled
if err != nil {
if os.IsNotExist(err) {
return "", false, nil
}
return "", false, fmt.Errorf("unable to read plugin file: %w", err)
}
// 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
}View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the file (cat ~/.claude/plugins/installed_plugins.json) and fix the JSON syntax
- Delete the corrupt file and reinstall the beads Claude plugin so Claude regenerates it
- Restart the Claude client/plugin marketplace to trigger a clean rewrite of the manifest
- Validate with a JSON linter (jq . < file) before rerunning bd doctor
Example fix
# before $ cat ~/.claude/plugins/installed_plugins.json # empty or truncated # after rm ~/.claude/plugins/installed_plugins.json # reinstall beads plugin via Claude, then rerun bd doctor
Defensive patterns
Strategy: validation
Validate before calling
import "encoding/json", "os"
func manifestIsValid(home string) error {
p := filepath.Join(home, ".claude", "plugins", "installed_plugins.json")
data, err := os.ReadFile(p)
if err != nil { return err }
if len(data) == 0 { return errors.New("manifest is empty") }
var v any
return json.Unmarshal(data, &v)
} Try / catch
_, installed, err := GetClaudePluginVersion()
if err != nil && strings.Contains(err.Error(), "unable to parse plugin file:") {
log.Printf("corrupt manifest: %v — reinstall plugin", err)
return nil
} Prevention
- Never hand-edit installed_plugins.json while Claude is running
- Validate JSON with jq before saving manual changes
- Reinstall the plugin after crashes that may truncate writes
- Back up the manifest before plugin updates
When it happens
Trigger: GetClaudePluginVersion (via CheckClaudePlugin) when the installed_plugins.json content fails json.Unmarshal — empty file, truncated write, or arbitrary non-JSON bytes — at cmd/bd/doctor/claude.go:558.
Common situations: A Claude plugin update was interrupted mid-write leaving a truncated/empty file; the file was hand-edited and corrupted; another tool overwrote it with logs or binary data.
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 v2: %w
- failed to write JSON: %w
- unable to determine home directory: %w
- unable to read plugin file: %w
- parse gh output: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/084bbb6471565413.
Report an issue: GitHub.