{"record":{"id":"ed432e7b4ec2c42b","repo":"gastownhall/beads","slug":"failed-to-get-stale-issues-w","errorCode":null,"errorMessage":"failed to get stale issues: %w","messagePattern":"failed to get stale issues: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/stale.go","lineNumber":66,"sourceCode":"\t\t  )%s\n\t\tORDER BY updated_at ASC\n\t`, statusClause, labelClause)\n\targs := []interface{}{cutoff}\n\tif filter.Status != \"\" {\n\t\targs = append(args, filter.Status)\n\t}\n\targs = append(args, cutoff) // NOT EXISTS heartbeat cutoff, after any status arg\n\t// Label placeholders sit after the NOT EXISTS in the query text, so their\n\t// args go last.\n\targs = append(args, labelArgs...)\n\n\tif filter.Limit > 0 {\n\t\tquery += fmt.Sprintf(\" LIMIT %d\", filter.Limit)\n\t}\n\n\trows, err := tx.QueryContext(ctx, query, args...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get stale issues: %w\", err)\n\t}\n\n\t// Collect IDs first, then batch-fetch full issues.\n\t// Close rows explicitly before the nested fetch — MySQL/Dolt drivers\n\t// can't handle multiple active result sets on one connection.\n\tvar ids []string\n\tfor rows.Next() {\n\t\tvar id string\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\trows.Close()\n\t\t\treturn nil, fmt.Errorf(\"failed to scan stale issue id: %w\", err)\n\t\t}\n\t\tids = append(ids, id)\n\t}\n\tif err := rows.Err(); err != nil {\n\t\trows.Close()\n\t\treturn nil, fmt.Errorf(\"stale issues rows: %w\", err)\n\t}","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/stale.go#L48-L84","documentation":"GetStaleIssuesInTx builds and runs a SQL query selecting issue IDs not updated within the filter window (and without a live heartbeat), and wraps any QueryContext failure with this message. It indicates the database rejected or failed to execute the stale-issues SELECT, before any rows were read. The underlying driver error is preserved via %w.","triggerScenarios":"Calling GetStaleIssuesInTx when the transaction/connection is broken or closed, the issues or leases table is missing or corrupted, the SQL text plus interpolated LIMIT and label clauses is malformed, or the context is cancelled/times out mid-query.","commonSituations":"Running against a database where a migration didn't create the leases table; a context deadline exceeded during a large stale scan; Dolt server restarts mid-transaction; passing a StaleFilter with unusual label sets that produce a bad clause.","solutions":["Inspect the wrapped driver error to identify the root cause (missing table, connection lost, cancelled context).","Verify the database schema is current (issues and leases tables exist with expected columns); run the app's migration/doctor command.","Check that the *sql.Tx passed in is still open and its connection is alive; retry the whole operation with a fresh transaction if the connection dropped.","If it's a context deadline, raise the timeout or narrow the filter (smaller Days window or a Limit) so the scan is cheaper."],"exampleFix":"// before\nissues, err := GetStaleIssuesInTx(ctx, brokenTx, filter)\n// after\ntx, err := store.BeginTx(ctx)\nif err != nil { return err }\nctx, cancel := context.WithTimeout(ctx, 30*time.Second)\ndefer cancel()\nissues, err := GetStaleIssuesInTx(ctx, tx, filter)","handlingStrategy":"retry","validationCode":"// caller-side pre-check\nif tx == nil { return fmt.Errorf(\"no active transaction\") }\nif err := ctx.Err(); err != nil { return fmt.Errorf(\"context already done: %w\", err) }","typeGuard":null,"tryCatchPattern":"issues, err := issueops.GetStaleIssuesInTx(ctx, tx, filter)\nif err != nil {\n    var retryable = errors.Is(err, sql.ErrConnDone) || ctx.Err() == nil\n    if retryable { /* retry with fresh tx */ }\n    return fmt.Errorf(\"stale scan failed: %w\", err)\n}","preventionTips":["Always pass a live tx and a context with a sane timeout","Run schema migrations before issuing stale queries","Wrap long scans in retry-with-backoff on transient driver errors","Keep the StaleFilter narrow (Limit/Days) on large databases"],"tags":["go","sql","database","query"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}