{"record":{"id":"c3744a232703a41b","repo":"gastownhall/beads","slug":"failed-to-commit-is-blocked-repairs-w","errorCode":null,"errorMessage":"failed to commit is_blocked repairs: %w","messagePattern":"failed to commit is_blocked repairs: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/doctor/fix/blocked.go","lineNumber":71,"sourceCode":"\t\treturn fmt.Errorf(\"failed to begin transaction: %w\", err)\n\t}\n\t// Refuse to derive and commit is_blocked from a dirty graph: like the store\n\t// paths, the recompute reads the working set and stages only `issues`, so a\n\t// dirty issues/dependencies tree would taint the repair commit (bd-6dnrw.37).\n\t// In a `bd doctor --fix` run the dependency-graph fixes commit ahead of this\n\t// one, so the tree is normally clean here; when it is not, surface it as an\n\t// actionable error rather than committing tainted state.\n\tif err := issueops.GuardBlockedRecomputeWorkingSet(ctx, tx); err != nil {\n\t\t_ = tx.Rollback()\n\t\treturn err\n\t}\n\tchanged, err := issueops.RecomputeAllIsBlockedInTx(ctx, tx)\n\tif err != nil {\n\t\t_ = tx.Rollback()\n\t\treturn fmt.Errorf(\"failed to recompute is_blocked: %w\", err)\n\t}\n\tif err := tx.Commit(); err != nil {\n\t\treturn fmt.Errorf(\"failed to commit is_blocked repairs: %w\", err)\n\t}\n\n\tif changed == 0 {\n\t\tfmt.Println(\"  is_blocked already consistent — nothing to fix\")\n\t\treturn nil\n\t}\n\n\t// Persist the corrected flags as a Dolt commit, staging only issues — the\n\t// synced table is_blocked lives on (wisps are dolt_ignore'd). This path keeps\n\t// its own fresh-DB lifecycle rather than the shared store helper, but it must\n\t// not report success on a failed commit: a swallowed DOLT_COMMIT error would\n\t// leave the repair in the working set only, silently undone by the next pull.\n\t// bd doctor is server-mode only, so the server supplies the commit identity.\n\tif _, err := db.ExecContext(ctx, \"CALL DOLT_ADD(?)\", \"issues\"); err != nil {\n\t\treturn fmt.Errorf(\"failed to stage is_blocked repairs: %w\", err)\n\t}\n\tif _, err := db.ExecContext(ctx, \"CALL DOLT_COMMIT('-m', 'doctor: recompute is_blocked for all issues')\"); err != nil && !issueops.IsNothingToCommitError(err) {\n\t\treturn fmt.Errorf(\"failed to commit is_blocked repairs to Dolt: %w\", err)","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/doctor/fix/blocked.go#L53-L89","documentation":"After the is_blocked recompute succeeds inside the transaction, repairBlockedState calls tx.Commit() to persist the corrected rows to the working set. This error wraps a failed commit; the transaction's changes are lost and nothing was repaired. Note it does not roll back explicitly (commit failure already terminates the tx), and no DOLT_ADD/DOLT_COMMIT staging happens afterward — so the failure is contained to the SQL transaction layer.","triggerScenarios":"Calling fix.RecomputeBlocked when tx.Commit() fails: the connection to the Dolt server dropped between recompute and commit, the server rejected the commit (lock conflict, deadlock, session killed), the context/deadline expired mid-commit, or the server is read-only (--read-only replica) and refuses writes.","commonSituations":"Long recompute exceeded a connection idle timeout so the session died before commit; another process held conflicting locks on the issues table; pointing bd at a read-only Dolt replica or a server started read-only; network blip between client and server during a large update.","solutions":["Read the wrapped %w cause; if it is a dead/timed-out connection, simply rerun `bd doctor --fix` with a fresh connection.","Ensure the Dolt server is writable — a read-only server or replica will reject the commit; point bd at the primary.","Rerun the fix when contention is suspected: commit failures from lock conflicts resolve once the competing process finishes.","For large databases timing out, increase connection idle/timeout settings or run the repair against the server directly on localhost."],"exampleFix":"// before (read-only replica)\nerr: failed to commit is_blocked repairs: Error 1227: server is running with --read-only\n\n// after: target the writable primary\nbeadsDir = primaryBeadsDir // not the replica checkout\nerr := fix.RecomputeBlocked(beadsDir)","handlingStrategy":"retry","validationCode":"// Ensure the target server accepts writes before the repair\nvar readonly int\nif err := db.QueryRow(\"SELECT @@read_only\").Scan(&readonly); err == nil && readonly == 1 {\n\tlog.Fatal(\"Dolt server is read-only — point bd at the writable primary\")\n}","typeGuard":"func isDeadConnCommitErr(err error) bool {\n\treturn err != nil && (errors.Is(err, sql.ErrConnDone) ||\n\t\tstrings.Contains(err.Error(), \"bad connection\") ||\n\t\tstrings.Contains(err.Error(), \"driver: timeout\"))\n}","tryCatchPattern":"if err := repairBlockedState(ctx, db); err != nil && isDeadConnCommitErr(err) {\n\t// recompute rolled back with the tx — safe to retry on a fresh handle\n\tdb.Close()\n\tif db, _, err = openDoltDB(beadsDir); err == nil {\n\t\terr = repairBlockedState(ctx, db)\n\t}\n}","preventionTips":["Avoid idle timeouts on long recomputes — keep a local/fast connection to the server","Never point bd at a read-only replica for doctor --fix","Check for competing lock holders on the issues table before fixing","Set generous context deadlines for the repair on large databases"],"tags":["go","database","dolt","sql","transaction"],"backgroundTag":"transaction-commit-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}