gastownhall/beads · error

db: DependencySQLRepository.Insert: mark is_blocked: %w

Error message

db: DependencySQLRepository.Insert: mark is_blocked: %w

What it means

This error wraps a failure from markDirectBlockedSource, which flags the source issue as directly blocked after adding a blocks/conditional-blocks edge. The edge insert succeeded but the direct is_blocked marker update failed.

Source

Thrown at internal/storage/domain/db/dependency.go:210

	// target spawner), then a Mark pass propagates blocked state — or, for
	// parent-child adds (not monotonic: an already-closed child can satisfy an
	// any-children waits-for gate), a full mark/unmark Recompute. Skipping the
	// expansion left descendants stale when a blocking edge landed on their
	// ancestor (bd-6dnrw.44 item 3).
	srcIsWisp := opts.UseWispsTable
	var affectedIssues, affectedWisps []string
	var aerr error
	if srcIsWisp {
		affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeForWispInTx(ctx, r.runner, dep.IssueID, dep.DependsOnID, dep.Type)
	} else {
		affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeInTx(ctx, r.runner, dep.IssueID, dep.DependsOnID, dep.Type)
	}
	if aerr != nil {
		return fmt.Errorf("db: DependencySQLRepository.Insert: affected set: %w", aerr)
	}
	if dep.Type == types.DepBlocks || dep.Type == types.DepConditionalBlocks {
		if err := r.markDirectBlockedSource(ctx, dep.IssueID, srcIsWisp, dep.DependsOnID, targetCol); err != nil {
			return fmt.Errorf("db: DependencySQLRepository.Insert: mark is_blocked: %w", err)
		}
		affectedIssues, affectedWisps = issueops.RemoveSourceFromAffected(dep.IssueID, srcIsWisp, affectedIssues, affectedWisps)
	}
	if dep.Type == types.DepParentChild {
		if err := issueops.RecomputeIsBlockedInTx(ctx, r.runner, affectedIssues, affectedWisps); err != nil {
			return fmt.Errorf("db: DependencySQLRepository.Insert: recompute is_blocked: %w", err)
		}
		// Snapshot only after all derived blocked-state maintenance has completed.
		return issueops.RecordDepEventInTx(ctx, r.runner, issueops.EventDepAdd, dep.IssueID, string(dep.Type), dep.DependsOnID, metadata, actor)
	}
	if err := issueops.MarkIsBlockedInTx(ctx, r.runner, affectedIssues, affectedWisps); err != nil {
		return fmt.Errorf("db: DependencySQLRepository.Insert: mark is_blocked (affected): %w", err)
	}
	// Snapshot only after all derived blocked-state maintenance has completed.
	// Never gated on opts.EmitEvent: a structurally-wired edge is as real to a
	// replaying consumer as one added by an explicit dep verb.
	return issueops.RecordDepEventInTx(ctx, r.runner, issueops.EventDepAdd, dep.IssueID, string(dep.Type), dep.DependsOnID, metadata, actor)
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check connectivity and retry; re-running Insert is safe for same-type edges (idempotent metadata refresh).
  2. Verify is_blocked columns exist in the current schema (bd doctor).
  3. Run a blocked-state recompute to repair stale flags after the failure.
  4. Inspect the wrapped driver error for the failing statement.
Defensive patterns

Strategy: retry

Try / catch

if err := repo.Insert(ctx, dep, actor, opts); err != nil {
    if strings.Contains(err.Error(), "mark is_blocked") && !strings.Contains(err.Error(), "(affected)") {
        // direct mark failed; retry and verify is_blocked state
    }
}

Prevention

When it happens

Trigger: Calling Insert with dep.Type = DepBlocks or DepConditionalBlocks where markDirectBlockedSource's UPDATE of the source issue's is_blocked flag fails (driver error, missing column, connection loss).

Common situations: Schema drift on issues/wisps is_blocked columns; connection dropped between insert and mark; read-only backend.

Related errors


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