{"record":{"id":"967f645aff035139","repo":"gastownhall/beads","slug":"read-is-blocked-from-s-w","errorCode":null,"errorMessage":"read is_blocked from %s: %w","messagePattern":"read is_blocked from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependency_queries.go","lineNumber":922,"sourceCode":"//\n//nolint:gosec // G201: table names are hardcoded constants.\nfunc IsBlockedInTx(ctx context.Context, tx DBTX, issueID string) (bool, []string, error) {\n\tvar blocked bool\n\tfound := false\n\tfor _, table := range []string{\"issues\", \"wisps\"} {\n\t\tvar b int\n\t\t//nolint:gosec // G201: table is a hardcoded \"issues\" or \"wisps\".\n\t\terr := tx.QueryRowContext(ctx, \"SELECT is_blocked FROM \"+table+\" WHERE id = ?\", issueID).Scan(&b)\n\t\tif err == nil {\n\t\t\tblocked = b != 0\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t\tif !errors.Is(err, sql.ErrNoRows) {\n\t\t\tif optionalBlockedTable(table) && isTableNotExistError(err) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn false, nil, fmt.Errorf(\"read is_blocked from %s: %w\", table, err)\n\t\t}\n\t}\n\tif !found || !blocked {\n\t\treturn false, nil, nil\n\t}\n\n\ttype depEdge struct {\n\t\tdependsOnID, depType string\n\t}\n\tvar edges []depEdge\n\tfor _, depTable := range []string{\"dependencies\", \"wisp_dependencies\"} {\n\t\trows, err := tx.QueryContext(ctx, fmt.Sprintf(`\n\t\t\tSELECT %s AS depends_on_id, type FROM %s\n\t\t\tWHERE issue_id = ? AND type IN ('blocks', 'waits-for', 'conditional-blocks')\n\t\t`, DepTargetExpr, depTable), issueID)\n\t\tif err != nil {\n\t\t\tif optionalBlockedTable(depTable) && isTableNotExistError(err) {\n\t\t\t\tcontinue","sourceCodeStart":904,"sourceCodeEnd":940,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_queries.go#L904-L940","documentation":"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.","triggerScenarios":"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.","commonSituations":"Failed/partial migrations leaving a required dep table absent (on non-optional variants); corrupted table; driver-level query errors; permission problems.","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"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// confirm is_blocked column exists before close flows\n_, err := db.Query(\"SELECT id, is_blocked FROM deps LIMIT 1\")\nif err != nil { /* run migrations */ }","typeGuard":null,"tryCatchPattern":"err := tx.CloseIssue(ctx, id)\nif err != nil {\n\tvar retryable bool\n\tif errors.Is(err, sql.ErrNoRows) { retryable = false }\n\tif isLockTimeout(err) { retryable = true }\n\t_ = retryable\n\treturn err\n}","preventionTips":["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"],"tags":["go","sql","storage","is-blocked"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}