gastownhall/beads · error

read is_blocked from %s: %w

Error message

read is_blocked from %s: %w

What it means

Wraps a SQL failure while reading the is_blocked column for an issue inside IsBlockedInTx. Non-'no-rows' query errors that are not an ignorable missing optional table abort the blocked check with this wrapped error. Callers of IsBlocked (e.g. close policy enforcement) will see it when deciding whether an issue can be closed.

Source

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

//
//nolint:gosec // G201: table names are hardcoded constants.
func IsBlockedInTx(ctx context.Context, tx DBTX, issueID string) (bool, []string, error) {
	var blocked bool
	found := false
	for _, table := range []string{"issues", "wisps"} {
		var b int
		//nolint:gosec // G201: table is a hardcoded "issues" or "wisps".
		err := tx.QueryRowContext(ctx, "SELECT is_blocked FROM "+table+" WHERE id = ?", issueID).Scan(&b)
		if err == nil {
			blocked = b != 0
			found = true
			break
		}
		if !errors.Is(err, sql.ErrNoRows) {
			if optionalBlockedTable(table) && isTableNotExistError(err) {
				continue
			}
			return false, nil, fmt.Errorf("read is_blocked from %s: %w", table, err)
		}
	}
	if !found || !blocked {
		return false, nil, nil
	}

	type depEdge struct {
		dependsOnID, depType string
	}
	var edges []depEdge
	for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
		rows, err := tx.QueryContext(ctx, fmt.Sprintf(`
			SELECT %s AS depends_on_id, type FROM %s
			WHERE issue_id = ? AND type IN ('blocks', 'waits-for', 'conditional-blocks')
		`, DepTargetExpr, depTable), issueID)
		if err != nil {
			if optionalBlockedTable(depTable) && isTableNotExistError(err) {
				continue

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped driver error to identify the failure
  2. Run pending migrations so required dependency tables exist with is_blocked columns
  3. If the table should be optional, confirm the table name matches the optionalBlockedTable list
  4. Repair or recreate the table and retry the close operation
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm is_blocked column exists before close flows
_, err := db.Query("SELECT id, is_blocked FROM deps LIMIT 1")
if err != nil { /* run migrations */ }

Try / catch

err := tx.CloseIssue(ctx, id)
if err != nil {
	var retryable bool
	if errors.Is(err, sql.ErrNoRows) { retryable = false }
	if isLockTimeout(err) { retryable = true }
	_ = retryable
	return err
}

Prevention

When it happens

Trigger: Calling IsBlocked / close-policy enforcement when the SELECT id, is_blocked query against a dependency table fails with an error other than sql.ErrNoRows and the table is not an optional table safely missing.

Common situations: Failed/partial migrations leaving a required dep table absent (on non-optional variants); corrupted table; driver-level query errors; permission problems.

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/967f645aff035139. Report an issue: GitHub.