{"record":{"id":"ecb2db16288ec416","repo":"gastownhall/beads","slug":"failed-to-parse-backup-state-w","errorCode":null,"errorMessage":"failed to parse backup state: %w","messagePattern":"failed to parse backup state: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/bd/backup_export.go","lineNumber":68,"sourceCode":"\tif err := os.MkdirAll(dir, 0700); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create backup directory: %w\", err)\n\t}\n\treturn dir, nil\n}\n\n// loadBackupState reads the backup state file, returning a zero state if missing.\nfunc loadBackupState(dir string) (*backupState, error) {\n\tpath := filepath.Join(dir, \"backup_state.json\")\n\tdata, err := os.ReadFile(path) //nolint:gosec // path is constructed internally\n\tif os.IsNotExist(err) {\n\t\treturn &backupState{}, nil\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read backup state: %w\", err)\n\t}\n\tvar state backupState\n\tif err := json.Unmarshal(data, &state); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse backup state: %w\", err)\n\t}\n\treturn &state, nil\n}\n\n// saveBackupState writes the backup state file atomically.\nfunc saveBackupState(dir string, state *backupState) error {\n\tdata, err := json.MarshalIndent(state, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal backup state: %w\", err)\n\t}\n\treturn atomicWriteFile(filepath.Join(dir, \"backup_state.json\"), data)\n}\n\n// atomicWriteFile writes data to a same-directory temp file, fsyncs the\n// temp file's own contents, then renames it into place. This avoids a\n// truncated/partial file at path if the process crashes mid-write.\n//\n// Two caveats this does NOT cover, narrowing the \"crash-safe\" claim rather","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/backup_export.go#L50-L86","documentation":"loadBackupState wraps json.Unmarshal failures when .beads/backup_state.json exists but is not valid JSON for the backupState struct. The throttle state is corrupt, so the auto-backup machinery cannot determine the last backup time/commit.","triggerScenarios":"backup_state.json contains truncated content (interrupted write from an older non-atomic version), an empty file, invalid characters, or JSON whose types don't match backupState fields.","commonSituations":"Power loss or kill -9 during a legacy write that predated atomicWriteFile; manual editing of .beads files; file truncated to 0 bytes by a crash or sync tool; copying repos with partially synced .beads state.","solutions":["Delete the corrupt state file: rm .beads/backup_state.json — the next command recreates it and worst case performs an extra backup.","Inspect the file (cat .beads/backup_state.json) to confirm corruption before deleting; it contains only throttle timestamps/commit hashes, no issue data.","If corruption recurs, ensure bd isn't being killed mid-write (check for OOM kills, abrupt shutdowns) — modern versions write atomically via atomicWriteFile.","No data-loss risk: this file only governs backup throttling; your issues live in Dolt."],"exampleFix":"// shell\nrm -f .beads/backup_state.json && bd sync  # state is regenerated with fresh throttle timestamps","handlingStrategy":"validation","validationCode":"const p = '.beads/backup_state.json'\ndata, _ := os.ReadFile(p)\nif len(data) > 0 {\n    var probe map[string]any\n    if err := json.Unmarshal(data, &probe); err != nil {\n        os.Remove(p) // corrupt throttle state; safe to remove, no issue data\n    }\n}","typeGuard":"func isValidBackupState(b []byte) bool {\n    var s backupState\n    return json.Unmarshal(b, &s) == nil\n}","tryCatchPattern":"state, err := loadBackupState(dir)\nif err != nil {\n    if strings.Contains(err.Error(), \"parse backup state\") {\n        _ = os.Remove(filepath.Join(dir, \"backup_state.json\"))\n        state = &backupState{} // regenerate; worst case an extra backup runs\n    } else {\n        return err\n    }\n}","preventionTips":["Don't hand-edit files under .beads/","Avoid killing bd with SIGKILL during sync; let writes finish","Keep bd up to date — modern versions write state atomically","Don't rsync/partial-copy .beads directories between machines"],"tags":["go","json","corruption","backup"],"backgroundTag":"json-parse-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}