{"record":{"id":"8235c13ec8ccc15b","repo":"gastownhall/beads","slug":"parse-s-w-8235c1","errorCode":null,"errorMessage":"parse %s: %w","messagePattern":"parse (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/doltutil/remotes.go","lineNumber":104,"sourceCode":"// means \"not a dolt repository here\" and returns (nil, nil); an unreadable or\n// unparseable file returns an error so callers can tell \"definitely none\"\n// from \"could not tell\". Results are sorted by name.\nfunc PersistedRemotes(dbPath string) ([]storage.RemoteInfo, error) {\n\tpath := filepath.Join(dbPath, \".dolt\", \"repo_state.json\")\n\tdata, err := os.ReadFile(path) // #nosec G304 -- repo-local dolt state file\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"read %s: %w\", path, err)\n\t}\n\tvar state struct {\n\t\tRemotes map[string]struct {\n\t\t\tURL string `json:\"url\"`\n\t\t} `json:\"remotes\"`\n\t}\n\tif err := json.Unmarshal(data, &state); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse %s: %w\", path, err)\n\t}\n\tremotes := make([]storage.RemoteInfo, 0, len(state.Remotes))\n\tfor name, r := range state.Remotes {\n\t\tremotes = append(remotes, storage.RemoteInfo{Name: name, URL: r.URL})\n\t}\n\tsort.Slice(remotes, func(i, j int) bool { return remotes[i].Name < remotes[j].Name })\n\treturn remotes, nil\n}\n\n// ListCLIRemotes parses `dolt remote -v` output from the given database\n// directory. This is a read-only guard for deciding whether CLI push/pull/fetch\n// can safely run from that directory; remote mutation still goes through SQL.\nfunc ListCLIRemotes(dbPath string) ([]storage.RemoteInfo, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), listCLIRemotesTimeout(dbPath))\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, \"dolt\", \"remote\", \"-v\") // #nosec G204 -- fixed command\n\tcmd.Dir = dbPath\n\tout, err := cmd.CombinedOutput()","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/doltutil/remotes.go#L86-L122","documentation":"PersistedRemotes parses .dolt/repo_state.json as JSON after reading it. If json.Unmarshal fails, this error wraps the parse failure with the file path, indicating the Dolt repo state file is malformed. The library cannot enumerate persisted remotes until the file is valid JSON.","triggerScenarios":"Calling PersistedRemotes when repo_state.json is corrupted: truncated write during a crash, manual editing mistakes, or encoding issues.","commonSituations":"Power loss / kill -9 mid-write; user hand-edited the file; Dolt version format change; disk corruption.","solutions":["Inspect <dbPath>/.dolt/repo_state.json and fix the JSON syntax manually.","Restore the file from a backup or a healthy clone of the same repo.","Let Dolt regenerate repo state (re-add remotes via 'dolt remote add') after moving the corrupt file aside.","Check disk health if corruption recurs."],"exampleFix":"// before\nmv .dolt/repo_state.json .dolt/repo_state.json.bak  # corrupt\n// after\nmv .dolt/repo_state.json.bak .dolt/repo_state.json  # restored valid JSON, then verify: dolt remote -v","handlingStrategy":"validation","validationCode":"data, err := os.ReadFile(filepath.Join(dbPath, \".dolt\", \"repo_state.json\"))\nif err == nil && json.Valid(data) { /* safe to call PersistedRemotes */ }","typeGuard":null,"tryCatchPattern":"remotes, err := doltutil.PersistedRemotes(dbPath)\nif err != nil && strings.HasPrefix(err.Error(), \"parse \") {\n    os.Rename(statePath, statePath+\".corrupt\") // quarantine and rebuild\n    remotes = nil\n}","preventionTips":["Never hand-edit repo_state.json","Shut down cleanly (no kill -9) to avoid truncated writes","Keep backups of the .dolt directory","Check disk health if corruption recurs"],"tags":["json","parse","dolt","filesystem"],"backgroundTag":"json-parse-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}