gastownhall/beads · error
parsing legacy config: %w
Error message
parsing legacy config: %w
What it means
Load() in internal/configfile reads .beads/metadata.json; if absent, it falls back to the legacy config.json and migrates it. This error wraps a json.Unmarshal failure on that legacy file, meaning the old config.json exists but is not valid JSON (or has incompatible types for Config fields). Load aborts rather than silently dropping workspace settings.
Source
Thrown at internal/configfile/configfile.go:100
func Load(beadsDir string) (*Config, error) {
configPath := ConfigPath(beadsDir)
data, err := os.ReadFile(configPath) // #nosec G304 - controlled path from config
if os.IsNotExist(err) {
// Try legacy config.json location (migration path)
legacyPath := filepath.Join(beadsDir, "config.json")
data, err = os.ReadFile(legacyPath) // #nosec G304 - controlled path from config
if os.IsNotExist(err) {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("reading legacy config: %w", err)
}
// Migrate: parse legacy config, save as metadata.json, remove old file
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parsing legacy config: %w", err)
}
// Save to new location
if err := cfg.Save(beadsDir); err != nil {
return nil, fmt.Errorf("migrating config to metadata.json: %w", err)
}
// Remove legacy file (best effort: migration already saved to new location)
_ = os.Remove(legacyPath)
return &cfg, nil
}
if err != nil {
return nil, fmt.Errorf("reading config: %w", err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Validate/repair config.json with a JSON linter (jq . config.json) and fix syntax errors
- Check field value types against the Config struct in internal/configfile/configfile.go (e.g. database must be a string)
- Restore config.json from backup or version control
- Delete config.json and recreate config via bd init (migration will start fresh with defaults)
Example fix
// before
{ "database": 123, } // trailing comma, wrong type
// after
{ "database": "beads.db" } Defensive patterns
Strategy: validation
Validate before calling
data, err := os.ReadFile(filepath.Join(beadsDir, "config.json"))
if err == nil {
var probe map[string]any
if jerr := json.Unmarshal(data, &probe); jerr != nil {
return fmt.Errorf("legacy config.json is not valid JSON, fix before running bd: %w", jerr)
}
} Type guard
func isJSON(data []byte) bool {
var v any
return json.Unmarshal(data, &v) == nil
} Try / catch
cfg, err := configfile.Load(beadsDir)
if err != nil {
var syntaxErr *json.SyntaxTypeError
var typeErr *json.UnmarshalTypeError
switch {
case errors.As(err, &syntaxErr):
// point user at config.json offset in syntaxErr.Offset
case errors.As(err, &typeErr):
// wrong type at typeErr.Field
}
return err
} Prevention
- Never hand-edit config.json while bd may run concurrently
- Validate JSON after any manual edit (jq . config.json)
- Upgrade bd fully so legacy migration completes once and removes config.json
- Keep .beads under version control to restore corrupt files
When it happens
Trigger: Calling configfile.Load(beadsDir) when metadata.json is missing, config.json exists, and config.json contains malformed JSON (truncated write, hand-edited syntax error, non-JSON content) or fields whose JSON types don't match Config (e.g. "database": 123).
Common situations: Workspaces created by old bd versions migrated after a manual edit; interrupted writes to config.json from a crash or full disk; a user placing notes or YAML in config.json; older metadata fields with changed types.
Related errors
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/21940eb526812c74.
Report an issue: GitHub.