gastownhall/beads · error
read %s: %w
Error message
read %s: %w
What it means
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.
Source
Thrown at internal/storage/doltutil/remotes.go:96
strings.HasPrefix(url, "git+file://") ||
strings.HasPrefix(url, "git://")
}
// PersistedRemotes reads the Dolt remotes recorded in
// <dbPath>/.dolt/repo_state.json directly, without shelling out to the dolt
// CLI — so it works when the dolt binary is absent and its failure modes are
// distinguishable (bd-6dnrw.33). A missing .dolt directory or repo_state.json
// means "not a dolt repository here" and returns (nil, nil); an unreadable or
// unparseable file returns an error so callers can tell "definitely none"
// from "could not tell". Results are sorted by name.
func PersistedRemotes(dbPath string) ([]storage.RemoteInfo, error) {
path := filepath.Join(dbPath, ".dolt", "repo_state.json")
data, err := os.ReadFile(path) // #nosec G304 -- repo-local dolt state file
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("read %s: %w", path, err)
}
var state struct {
Remotes map[string]struct {
URL string `json:"url"`
} `json:"remotes"`
}
if err := json.Unmarshal(data, &state); err != nil {
return nil, fmt.Errorf("parse %s: %w", path, err)
}
remotes := make([]storage.RemoteInfo, 0, len(state.Remotes))
for name, r := range state.Remotes {
remotes = append(remotes, storage.RemoteInfo{Name: name, URL: r.URL})
}
sort.Slice(remotes, func(i, j int) bool { return remotes[i].Name < remotes[j].Name })
return remotes, nil
}
// ListCLIRemotes parses `dolt remote -v` output from the given databaseView on GitHub (pinned to 71377f2769)
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.
Defensive patterns
Strategy: try-catch
Validate before calling
st, err := os.Stat(filepath.Join(dbPath, ".dolt", "repo_state.json")) readable := err == nil && !st.IsDir() // note: missing file is fine (returns nil remotes); only unreadable files error
Try / catch
remotes, err := doltutil.PersistedRemotes(dbPath)
if err != nil {
return fmt.Errorf("cannot read dolt repo state: %w", err) // inspect path in message
} Prevention
- 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
When it happens
Trigger: Calling PersistedRemotes when .dolt/repo_state.json exists but is unreadable: permission denied, file locked, or an I/O error (bad disk).
Common situations: Repo state file owned by another user (ran bd with sudo previously); read-only mount; antivirus/backup lock on the file.
Related errors
- dolt path is not executable
- creating .bd-dolt-ok marker: %w
- failed to remove Dolt database: %w
- inspecting remote target %s: %w
- reading remote store %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b21426898c2d81f3.
Report an issue: GitHub.