{"record":{"id":"e1e699c8c97b9d6e","repo":"gastownhall/beads","slug":"merge-succeeded-but-is-blocked-recompute-failed","errorCode":null,"errorMessage":"merge succeeded but is_blocked recompute failed: %w","messagePattern":"merge succeeded but is_blocked recompute failed: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/store.go","lineNumber":4878,"sourceCode":"\t// bd-578h9.11: like every pull path, a branch merge brings in writes that\n\t// bypassed the local is_blocked hooks; recompute after a conflict-free\n\t// merge. Conflicted merges defer to the caller's post-resolution hook\n\t// (Sync, bd vc merge --strategy) — recomputing over unresolved rows would\n\t// read garbage.\n\tpreHead := \"\"\n\tif !s.readOnly {\n\t\tif h, err := s.GetCurrentCommit(ctx); err == nil {\n\t\t\tpreHead = h\n\t\t}\n\t}\n\n\tconflicts, err := versioncontrolops.Merge(ctx, s.db, branch, s.commitAuthorString())\n\tif len(conflicts) > 0 {\n\t\tspan.SetAttributes(attribute.Int(\"dolt.conflicts\", len(conflicts)))\n\t}\n\tif err == nil && len(conflicts) == 0 && !s.readOnly {\n\t\tif rerr := s.recomputeBlockedAfterPull(ctx, preHead); rerr != nil {\n\t\t\treturn conflicts, fmt.Errorf(\"merge succeeded but is_blocked recompute failed: %w\", rerr)\n\t\t}\n\t}\n\treturn conflicts, err\n}\n\n// MergeWithStrategy implements storage.StrategicMerger for `bd vc merge\n// --strategy` (#4992). Merge (above) runs the bare CALL DOLT_MERGE on the\n// shared pool: that is enough to detect a conflict-shaped autocommit\n// rejection, but not to resolve one, because Dolt's conflict-tolerant session\n// flags (@@dolt_allow_commit_conflicts, @@dolt_force_transaction_commit) are\n// session state and the pool may hand a later statement a different\n// connection. MergeWithStrategy instead pins a single connection — the same\n// pattern Branch/Checkout use for stored procedures — for the whole\n// merge/resolve/repair/commit sequence versioncontrolops.MergeWithStrategy\n// runs.\n//\n// A resolved merge (conflicted or clean) always commits, so — unlike Merge,\n// which skips the recompute for a still-conflicted merge — the is_blocked","sourceCodeStart":4860,"sourceCodeEnd":4896,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/store.go#L4860-L4896","documentation":"After a merge completes with zero conflicts, the store recomputes the denormalized is_blocked column (recomputeBlockedAfterPull). This error signals the merge itself succeeded but the post-merge recompute failed. It's deliberately distinct from the merge error so the developer knows branch state was updated but derived blocked-status data is now stale and needs a recompute.","triggerScenarios":"Merge with no conflicts on a writable (non-read-only) store, where recomputeBlockedAfterPull returns an error — its internal transaction/queries fail due to context cancellation, connection starvation, or schema issues with the issues table's is_blocked column.","commonSituations":"MaxOpenConns:1 pools where the recompute cannot get a connection; long merges that let the ctx expire before recompute; older databases missing columns the recompute expects.","solutions":["Re-run the recompute manually (RecomputeBlockedAfterMerge / bd equivalent) — merge state is fine, only derived data is stale.","Retry with a fresh, longer-lived context so the recompute isn't cut off.","Ensure the connection pool has capacity for the recompute's own connection.","Verify the issues schema is migrated (is_blocked column present).","Wrap follow-up merge calls so is_blocked staleness is detected and repaired on next run."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if store.IsReadOnly() { skip recompute } // recompute only runs when !s.readOnly\nif err := ctx.Err(); err != nil { /* use a fresh ctx for the recompute */ }","typeGuard":null,"tryCatchPattern":"conflicts, err := store.Merge(ctx, branch)\nvar recomputeErr *fmt.WrapError // or inspect message prefix\nif err != nil && strings.Contains(err.Error(), \"is_blocked recompute failed\") {\n    log.Warn(\"merge succeeded; recompute is_blocked and retry\", \"err\", err)\n    _ = store.RecomputeBlockedAfterMerge(ctx)\n}","preventionTips":["Run the recompute with a context independent of the merge request's deadline","Ensure pool capacity for merge + recompute connections","Keep schema migrations current for is_blocked","After catching this error, always run the recompute before trusting is_blocked queries"],"tags":["dolt","merge","recompute","denormalization"],"backgroundTag":"post-merge-recompute-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}