gastownhall/beads · error

parse %s: %w

Error message

parse %s: %w

What it means

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.

Source

Thrown at internal/storage/doltutil/remotes.go:104

// 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 database
// directory. This is a read-only guard for deciding whether CLI push/pull/fetch
// can safely run from that directory; remote mutation still goes through SQL.
func ListCLIRemotes(dbPath string) ([]storage.RemoteInfo, error) {
	ctx, cancel := context.WithTimeout(context.Background(), listCLIRemotesTimeout(dbPath))
	defer cancel()
	cmd := exec.CommandContext(ctx, "dolt", "remote", "-v") // #nosec G204 -- fixed command
	cmd.Dir = dbPath
	out, err := cmd.CombinedOutput()

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect <dbPath>/.dolt/repo_state.json and fix the JSON syntax manually.
  2. Restore the file from a backup or a healthy clone of the same repo.
  3. Let Dolt regenerate repo state (re-add remotes via 'dolt remote add') after moving the corrupt file aside.
  4. Check disk health if corruption recurs.

Example fix

// before
mv .dolt/repo_state.json .dolt/repo_state.json.bak  # corrupt
// after
mv .dolt/repo_state.json.bak .dolt/repo_state.json  # restored valid JSON, then verify: dolt remote -v
Defensive patterns

Strategy: validation

Validate before calling

data, err := os.ReadFile(filepath.Join(dbPath, ".dolt", "repo_state.json"))
if err == nil && json.Valid(data) { /* safe to call PersistedRemotes */ }

Try / catch

remotes, err := doltutil.PersistedRemotes(dbPath)
if err != nil && strings.HasPrefix(err.Error(), "parse ") {
    os.Rename(statePath, statePath+".corrupt") // quarantine and rebuild
    remotes = nil
}

Prevention

When it happens

Trigger: Calling PersistedRemotes when repo_state.json is corrupted: truncated write during a crash, manual editing mistakes, or encoding issues.

Common situations: Power loss / kill -9 mid-write; user hand-edited the file; Dolt version format change; disk corruption.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/8235c13ec8ccc15b. Report an issue: GitHub.