{"record":{"id":"f0f327adf55815d3","repo":"gastownhall/beads","slug":"errblockedrecomputedirtygraph","errorCode":"ErrBlockedRecomputeDirtyGraph","errorMessage":"is_blocked recompute needs a clean working set","messagePattern":"is_blocked recompute needs a clean working set","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/blocked_consistency.go","lineNumber":28,"sourceCode":"\t\"strings\"\n)\n\n// blockedRecomputeGraphTables are the version-controlled tables a full\n// is_blocked recompute reads and folds into its issues-only repair commit.\n//\n// The dolt-ignored wisp tables (wisps, wisp_dependencies) are deliberately\n// excluded. They are never staged or committed, so dirty wisp state cannot leak\n// into the repair commit; and because Dolt reports every ignored table as\n// perpetually \"modified\" in dolt_status, guarding on them would refuse the\n// recompute in any workspace that has ever created a wisp. is_blocked derived\n// from wisp state is the same working-set-derived value every local write path\n// already produces.\nvar blockedRecomputeGraphTables = []string{\"issues\", \"dependencies\"}\n\n// ErrBlockedRecomputeDirtyGraph reports that a full is_blocked recompute was\n// asked to run while its committable graph tables had uncommitted changes.\n// Callers wrap it with the offending table names.\nvar ErrBlockedRecomputeDirtyGraph = errors.New(\"is_blocked recompute needs a clean working set\")\n\n// GuardBlockedRecomputeWorkingSet refuses a full is_blocked recompute when the\n// committable graph tables (issues, dependencies) have uncommitted working-set\n// changes (bd-6dnrw.37). The recompute derives is_blocked from the current graph\n// and stages only `issues`, so running it dirty would either sweep unrelated\n// issue edits into the repair commit or commit flags derived from uncommitted\n// dependency edits that are not part of the same commit. Run it as the first\n// statement inside the recompute's own transaction so it sees exactly the\n// working set the recompute will read. Returns a wrapped\n// ErrBlockedRecomputeDirtyGraph naming the dirty tables, or nil when clean.\nfunc GuardBlockedRecomputeWorkingSet(ctx context.Context, tx DBTX) error {\n\tdirty, err := dirtyBlockedRecomputeGraphTables(ctx, tx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"check working set before is_blocked recompute: %w\", err)\n\t}\n\tif len(dirty) == 0 {\n\t\treturn nil\n\t}","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/blocked_consistency.go#L10-L46","documentation":"ErrBlockedRecomputeDirtyGraph signals that a full is_blocked recompute was requested while the committable graph tables (issues, dependencies) had uncommitted working-set changes. Running the recompute dirty would sweep unrelated edits into the repair commit or commit flags derived from uncommitted data, so GuardBlockedRecomputeWorkingSet refuses; callers wrap the sentinel with the offending table names.","triggerScenarios":"Calling the full is_blocked recompute (RecomputeAllBlocked path) when GuardBlockedRecomputeWorkingSet finds dirty rows in the 'issues' or 'dependencies' tables — e.g. uncommitted issue edits or dependency changes staged in the working set.","commonSituations":"A `bd` repair/consistency command run while other uncommitted changes exist in the working set; a crash or interrupted command left staged changes; concurrent edits not yet committed to Dolt.","solutions":["Commit or discard the pending working-set changes to issues/dependencies, then rerun the recompute","Inspect the wrapped error for the offending table names and commit only that table's changes via the normal commit path","Use isRecomputeDirtyGraphErr/dirtyGraphErr to detect this condition programmatically and prompt the user to commit first"],"exampleFix":"// before\nif err := recomputeAllBlocked(ctx); err != nil { return err }\n// after\nif err := recomputeAllBlocked(ctx); err != nil {\n    if isRecomputeDirtyGraphErr(err) {\n        return fmt.Errorf(\"commit pending changes first: %w\", err)\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// before running the recompute, ensure the working set is clean:\n// commit or discard pending changes to issues and dependencies tables","typeGuard":"func isDirtyGraphErr(err error) bool { return isRecomputeDirtyGraphErr(err) }","tryCatchPattern":"if err := recomputeAllBlocked(ctx); err != nil {\n    if isRecomputeDirtyGraphErr(err) {\n        return fmt.Errorf(\"commit or discard pending issues/dependencies changes, then retry: %w\", err)\n    }\n    return err\n}","preventionTips":["Run is_blocked recomputes only when the working set is clean","Handle ErrBlockedRecomputeDirtyGraph with errors.Is/isRecomputeDirtyGraphErr, not string matching","After crashes or interrupted commands, check for staged issues/dependencies changes before repair operations"],"tags":["go","embedded-dolt","consistency","working-set"],"backgroundTag":"dirty-working-set","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}