gastownhall/beads · error
get issue: %w
Error message
get issue: %w
What it means
getIssueFromTableInTx failed while executing the hydration SELECT (issue row joined with lease info) for a reason other than no-rows or a missing optional table; the error is wrapped as "get issue". This is a database-level failure, not an absent issue — check the wrapped driver error.
Source
Thrown at internal/storage/issueops/get_issue.go:55
// missingOptionalIssueTable reports whether err is the absence of the optional
// issue plane the hydration query just read. The hydration FROM clause also
// carries sqlbuild.LeaseJoin, so a blanket table-not-exist check here folds a
// missing leases table into "row absent" — a wrong answer, not an empty one.
func missingOptionalIssueTable(err error, issueTable string) bool {
return optionalBlockedTable(issueTable) && dberrors.IsMissingTable(err, issueTable)
}
func getIssueFromTableInTx(ctx context.Context, tx DBTX, issueTable, labelTable, id string) (*types.Issue, error) {
//nolint:gosec // G201: issueTable is a hardcoded literal supplied by GetIssueInTx ("issues" or "wisps")
row := tx.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM %s %s WHERE id = ?`,
IssueSelectColumns, issueTable, sqlbuild.LeaseJoin(issueTable)), id)
issue, err := ScanIssueFrom(row)
if err == sql.ErrNoRows || missingOptionalIssueTable(err, issueTable) {
return nil, storage.ErrNotFound
}
if err != nil {
return nil, fmt.Errorf("get issue: %w", err)
}
// Fetch labels in the same transaction to avoid MaxOpenConns=1 deadlock.
labels, err := GetLabelsInTx(ctx, tx, labelTable, id)
if err != nil {
return nil, fmt.Errorf("get issue labels: %w", err)
}
issue.Labels = labels
return issue, nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped driver error to identify the root cause
- Run pending schema migrations so issues/leases/labels match expectations
- Retry the transaction if the error was transient
- Confirm the transaction is still active before the call
Example fix
// before
issue, err := issueops.GetIssueInTx(ctx, tx, id) // fails: unknown column in 'issues'
// after
if err := migrate(ctx, db); err != nil { // align schema with binary
return err
}
issue, err := issueops.GetIssueInTx(ctx, tx, id) Defensive patterns
Strategy: retry
Validate before calling
// ensure the non-optional tables exist before lookup
for _, tbl := range []string{"issues", "leases", "labels"} {
if _, err := tx.QueryContext(ctx, fmt.Sprintf(`SELECT 1 FROM %s LIMIT 1`, tbl)); err != nil {
return fmt.Errorf("table %s unavailable: %w", tbl, err)
}
} Try / catch
issue, err := issueops.GetIssueInTx(ctx, tx, id)
if err != nil && !errors.Is(err, storage.ErrNotFound) {
if isTransient(err) {
return retryGetIssue(ctx, tx, id)
}
log.Printf("hydration query for %s failed: %v", id, err)
return err
} Prevention
- Run migrations on startup so issues/leases/labels match the binary
- Retry transient driver errors with backoff before failing
- Distinguish ErrNotFound (absent row) from this wrapper (query failure) via errors.Is
When it happens
Trigger: ScanIssueFrom/QueryRow returns a driver error: table missing (non-optional tables like issues/leases), schema mismatch, connection failure, or aborted transaction.
Common situations: Schema drift after upgrade (columns added/renamed); Dolt server connection dropped mid-transaction; missing non-optional table such as leases in a partially migrated database.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- failed to recompute is_blocked: %w
- scan federation peer: %w
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
- failed to query orphaned dependencies: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2ddf1c84df8be2b0.
Report an issue: GitHub.