gastownhall/beads · error

check remaining blockers from %s: %w

Error message

check remaining blockers from %s: %w

What it means

Wraps a SQL failure from querying a dependency table for remaining 'blocks' edges while computing newly-unblocked issues inside GetNewlyUnblockedByCloseInTx. The table name is embedded in the message so you know which dependency table (deps or deps_v2 style optional table) failed. It is thrown whenever tx.QueryContext returns an error that is not an ignorable missing-optional-table error, aborting the unblock computation and the surrounding transaction.

Source

Thrown at internal/storage/issueops/dependency_queries.go:850

		if end > len(candidateIDs) {
			end = len(candidateIDs)
		}
		batch := candidateIDs[start:end]
		placeholders, batchArgs := buildSQLInClause(batch)

		remainingByCandidate := make(map[string][]string, len(batch))
		remainingBlockerSet := make(map[string]struct{})
		for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
			//nolint:gosec // G201: depTable is hardcoded.
			depRows, err := tx.QueryContext(ctx, fmt.Sprintf(`
				SELECT issue_id, %s AS depends_on_id FROM %s
				WHERE issue_id IN (%s) AND type = 'blocks' AND %s != ?
			`, DepTargetExpr, depTable, placeholders, DepTargetExpr), append(batchArgs, closedIssueID)...)
			if err != nil {
				if optionalBlockedTable(depTable) && isTableNotExistError(err) {
					continue
				}
				return nil, fmt.Errorf("check remaining blockers from %s: %w", depTable, err)
			}
			for depRows.Next() {
				var candidateID, blockerID string
				if err := depRows.Scan(&candidateID, &blockerID); err != nil {
					_ = depRows.Close()
					return nil, fmt.Errorf("scan remaining blocker: %w", err)
				}
				remainingByCandidate[candidateID] = append(remainingByCandidate[candidateID], blockerID)
				remainingBlockerSet[blockerID] = struct{}{}
			}
			_ = depRows.Close()
			if err := depRows.Err(); err != nil {
				return nil, fmt.Errorf("remaining blocker rows from %s: %w", depTable, err)
			}
		}

		remainingBlockerIDs := make([]string, 0, len(remainingBlockerSet))
		for blockerID := range remainingBlockerSet {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped underlying error (%w) to identify the driver error code
  2. Verify the dependency table schema matches what this version of beads expects (run migrations/bd migrate)
  3. If the table is optional, confirm isTableNotExistError matched; if not, the table exists but is broken — inspect/repair it
  4. Retry the operation after resolving any transient lock or connection issue

Example fix

// before
rows, err := tx.QueryContext(ctx, q, args...)
if err != nil { return nil, err } // loses table context
// after
if err != nil {
	if optionalBlockedTable(depTable) && isTableNotExistError(err) { continue }
	return nil, fmt.Errorf("check remaining blockers from %s: %w", depTable, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling close/unblock flows
if err := bd.Doctor(ctx); err != nil { /* fix schema/storage first */ }
_ = db.QueryRow("SELECT COUNT(*) FROM deps").Scan(&n) // table must be queryable

Try / catch

err := tx.CloseIssue(ctx, id)
if err != nil {
	var wrapped interface{ Unwrap() error }
	if errors.Is(err, context.DeadlineExceeded) { /* retry */ }
	log.Fatalf("unblock check failed: %v", err)
}

Prevention

When it happens

Trigger: Calling GetNewlyUnblockedByCloseInTx after closing an issue when the SQL SELECT against one of the dependency tables fails: malformed schema, corrupted table, lock timeout, or driver-level query error other than table-not-exist on an optional table.

Common situations: Partial or failed migrations leaving a dependency table in a bad state; database file corruption in embedded Dolt/SQLite deployments; concurrent DDL during the transaction; permission errors on the dep table.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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