{"record":{"id":"2ddf1c84df8be2b0","repo":"gastownhall/beads","slug":"get-issue-w","errorCode":null,"errorMessage":"get issue: %w","messagePattern":"get issue: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/get_issue.go","lineNumber":55,"sourceCode":"\n// missingOptionalIssueTable reports whether err is the absence of the optional\n// issue plane the hydration query just read. The hydration FROM clause also\n// carries sqlbuild.LeaseJoin, so a blanket table-not-exist check here folds a\n// missing leases table into \"row absent\" — a wrong answer, not an empty one.\nfunc missingOptionalIssueTable(err error, issueTable string) bool {\n\treturn optionalBlockedTable(issueTable) && dberrors.IsMissingTable(err, issueTable)\n}\n\nfunc getIssueFromTableInTx(ctx context.Context, tx DBTX, issueTable, labelTable, id string) (*types.Issue, error) {\n\t//nolint:gosec // G201: issueTable is a hardcoded literal supplied by GetIssueInTx (\"issues\" or \"wisps\")\n\trow := tx.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM %s %s WHERE id = ?`,\n\t\tIssueSelectColumns, issueTable, sqlbuild.LeaseJoin(issueTable)), id)\n\tissue, err := ScanIssueFrom(row)\n\tif err == sql.ErrNoRows || missingOptionalIssueTable(err, issueTable) {\n\t\treturn nil, storage.ErrNotFound\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get issue: %w\", err)\n\t}\n\n\t// Fetch labels in the same transaction to avoid MaxOpenConns=1 deadlock.\n\tlabels, err := GetLabelsInTx(ctx, tx, labelTable, id)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get issue labels: %w\", err)\n\t}\n\tissue.Labels = labels\n\n\treturn issue, nil\n}\n","sourceCodeStart":37,"sourceCodeEnd":67,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/get_issue.go#L37-L67","documentation":"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.","triggerScenarios":"ScanIssueFrom/QueryRow returns a driver error: table missing (non-optional tables like issues/leases), schema mismatch, connection failure, or aborted transaction.","commonSituations":"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.","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"],"exampleFix":"// before\nissue, err := issueops.GetIssueInTx(ctx, tx, id) // fails: unknown column in 'issues'\n// after\nif err := migrate(ctx, db); err != nil { // align schema with binary\n    return err\n}\nissue, err := issueops.GetIssueInTx(ctx, tx, id)","handlingStrategy":"retry","validationCode":"// ensure the non-optional tables exist before lookup\nfor _, tbl := range []string{\"issues\", \"leases\", \"labels\"} {\n    if _, err := tx.QueryContext(ctx, fmt.Sprintf(`SELECT 1 FROM %s LIMIT 1`, tbl)); err != nil {\n        return fmt.Errorf(\"table %s unavailable: %w\", tbl, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"issue, err := issueops.GetIssueInTx(ctx, tx, id)\nif err != nil && !errors.Is(err, storage.ErrNotFound) {\n    if isTransient(err) {\n        return retryGetIssue(ctx, tx, id)\n    }\n    log.Printf(\"hydration query for %s failed: %v\", id, err)\n    return err\n}","preventionTips":["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"],"tags":["database","sql","schema","go"],"backgroundTag":"database-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}