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

  1. Check the actual structure of installed_plugins.json against the v2 schema (plugins: {name: [{version, scope}]}) and fix it
  2. Reinstall the beads plugin so Claude regenerates a schema-correct manifest
  3. Update bd to the latest version in case newer manifest variants are supported
  4. 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

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

Related errors


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