{"record":{"id":"b79ebe72b84ec681","repo":"gastownhall/beads","slug":"reading-legacy-config-w","errorCode":null,"errorMessage":"reading legacy config: %w","messagePattern":"reading legacy config: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/configfile/configfile.go","lineNumber":94,"sourceCode":"}\n\nfunc ConfigPath(beadsDir string) string {\n\treturn filepath.Join(beadsDir, ConfigFileName)\n}\n\nfunc Load(beadsDir string) (*Config, error) {\n\tconfigPath := ConfigPath(beadsDir)\n\n\tdata, err := os.ReadFile(configPath) // #nosec G304 - controlled path from config\n\tif os.IsNotExist(err) {\n\t\t// Try legacy config.json location (migration path)\n\t\tlegacyPath := filepath.Join(beadsDir, \"config.json\")\n\t\tdata, err = os.ReadFile(legacyPath) // #nosec G304 - controlled path from config\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"reading legacy config: %w\", err)\n\t\t}\n\n\t\t// Migrate: parse legacy config, save as metadata.json, remove old file\n\t\tvar cfg Config\n\t\tif err := json.Unmarshal(data, &cfg); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parsing legacy config: %w\", err)\n\t\t}\n\n\t\t// Save to new location\n\t\tif err := cfg.Save(beadsDir); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"migrating config to metadata.json: %w\", err)\n\t\t}\n\n\t\t// Remove legacy file (best effort: migration already saved to new location)\n\t\t_ = os.Remove(legacyPath)\n\n\t\treturn &cfg, nil\n\t}","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/configfile/configfile.go#L76-L112","documentation":"Load wraps a non-ENOENT os.ReadFile failure of the legacy .beads/config.json with \"reading legacy config: %w\". A missing legacy file is treated as no legacy config; this error means the file exists but is unreadable, blocking the migrate-to-metadata.json path.","triggerScenarios":"Load(config) when .beads/config.json exists but cannot be read — permission denied, it is a directory, or an I/O error occurs during the migration check.","commonSituations":"Legacy config left read-only after copying a workspace as another user; config.json partially restored from backup with broken permissions; disk/device errors.","solutions":["Fix permissions: `chmod u+r .beads/config.json` and confirm ownership","Verify it is a regular file, not a directory or symlink to a missing target","If the migration already completed, the legacy file can be archived/removed after confirming metadata.json exists"],"exampleFix":"// before\n-rw------- root config.json   # owned by root, unreadable\n// after\nsudo chown $USER .beads/config.json && chmod u+rw .beads/config.json","handlingStrategy":"try-catch","validationCode":"fi, err := os.Stat(filepath.Join(\".beads\", \"config.json\"))\nif err == nil && (!fi.Mode().IsRegular() || fi.Mode().Perm()&0o400 == 0) {\n    // legacy config unreadable: fix permissions before running bd\n}","typeGuard":null,"tryCatchPattern":"cfg, err := bd.Load(cfgOpts)\nif err != nil {\n    if errors.Is(err, fs.ErrPermission) { /* chown/chmod .beads/config.json */ }\n    return err\n}","preventionTips":["Preserve ownership/permissions when copying or restoring .beads directories","Complete the legacy->metadata.json migration once, then remove config.json deliberately","Check workspace file ownership after running bd as root/sudo"],"tags":["config","filesystem","migration"],"backgroundTag":"config-file-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}