{"record":{"id":"b21426898c2d81f3","repo":"gastownhall/beads","slug":"read-s-w-b21426","errorCode":null,"errorMessage":"read %s: %w","messagePattern":"read (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/doltutil/remotes.go","lineNumber":96,"sourceCode":"\t\tstrings.HasPrefix(url, \"git+file://\") ||\n\t\tstrings.HasPrefix(url, \"git://\")\n}\n\n// PersistedRemotes reads the Dolt remotes recorded in\n// <dbPath>/.dolt/repo_state.json directly, without shelling out to the dolt\n// CLI — so it works when the dolt binary is absent and its failure modes are\n// distinguishable (bd-6dnrw.33). A missing .dolt directory or repo_state.json\n// 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","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/doltutil/remotes.go#L78-L114","documentation":"PersistedRemotes reads .dolt/repo_state.json under the database path to report configured Dolt remotes. If the file exists but cannot be read (permissions, I/O error), this error wraps the os.ReadFile failure with the path. A missing file is intentionally treated as 'no remotes' (nil, nil), so this error means the file exists but is unreadable.","triggerScenarios":"Calling PersistedRemotes when .dolt/repo_state.json exists but is unreadable: permission denied, file locked, or an I/O error (bad disk).","commonSituations":"Repo state file owned by another user (ran bd with sudo previously); read-only mount; antivirus/backup lock on the file.","solutions":["Check permissions on <dbPath>/.dolt/repo_state.json and fix ownership/ACL (chown/chmod).","Verify the filesystem is writable/readable and not mounted read-only.","Close processes locking the file (backup/AV/indexers) and retry.","As a last resort restore the file from a healthy clone or let dolt regenerate repo state."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"st, err := os.Stat(filepath.Join(dbPath, \".dolt\", \"repo_state.json\"))\nreadable := err == nil && !st.IsDir()\n// note: missing file is fine (returns nil remotes); only unreadable files error","typeGuard":null,"tryCatchPattern":"remotes, err := doltutil.PersistedRemotes(dbPath)\nif err != nil {\n    return fmt.Errorf(\"cannot read dolt repo state: %w\", err) // inspect path in message\n}","preventionTips":["Run bd/beads consistently as the same user (avoid sudo mixing)","Don't put the database on read-only or network mounts","Exclude .dolt from aggressive AV/backup locking"],"tags":["filesystem","permissions","dolt"],"backgroundTag":"file-read-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}