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

  1. Inspect the file (cat ~/.claude/plugins/installed_plugins.json) and fix the JSON syntax
  2. Delete the corrupt file and reinstall the beads Claude plugin so Claude regenerates it
  3. Restart the Claude client/plugin marketplace to trigger a clean rewrite of the manifest
  4. 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

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

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/084bbb6471565413. Report an issue: GitHub.