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) {
continueView on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped driver error to identify the failure
- Run pending migrations so required dependency tables exist with is_blocked columns
- If the table should be optional, confirm the table name matches the optionalBlockedTable list
- 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
- Always run migrations immediately after upgrading beads
- Do not close issues while another bd process holds a write lock
- Check `bd doctor` output before scripted close operations
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
- delete: recompute is_blocked: %w
- count wisp dependencies: %w
- count labels: %w
- count wisp labels: %w
- count events: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/967f645aff035139.
Report an issue: GitHub.