gastownhall/beads · error

failed to get stale issues: %w

Error message

failed to get stale issues: %w

What it means

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.

Source

Thrown at internal/storage/issueops/stale.go:66

		  )%s
		ORDER BY updated_at ASC
	`, statusClause, labelClause)
	args := []interface{}{cutoff}
	if filter.Status != "" {
		args = append(args, filter.Status)
	}
	args = append(args, cutoff) // NOT EXISTS heartbeat cutoff, after any status arg
	// Label placeholders sit after the NOT EXISTS in the query text, so their
	// args go last.
	args = append(args, labelArgs...)

	if filter.Limit > 0 {
		query += fmt.Sprintf(" LIMIT %d", filter.Limit)
	}

	rows, err := tx.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("failed to get stale issues: %w", err)
	}

	// Collect IDs first, then batch-fetch full issues.
	// Close rows explicitly before the nested fetch — MySQL/Dolt drivers
	// can't handle multiple active result sets on one connection.
	var ids []string
	for rows.Next() {
		var id string
		if err := rows.Scan(&id); err != nil {
			rows.Close()
			return nil, fmt.Errorf("failed to scan stale issue id: %w", err)
		}
		ids = append(ids, id)
	}
	if err := rows.Err(); err != nil {
		rows.Close()
		return nil, fmt.Errorf("stale issues rows: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped driver error to identify the root cause (missing table, connection lost, cancelled context).
  2. Verify the database schema is current (issues and leases tables exist with expected columns); run the app's migration/doctor command.
  3. 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.
  4. If it's a context deadline, raise the timeout or narrow the filter (smaller Days window or a Limit) so the scan is cheaper.

Example fix

// before
issues, err := GetStaleIssuesInTx(ctx, brokenTx, filter)
// after
tx, err := store.BeginTx(ctx)
if err != nil { return err }
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
issues, err := GetStaleIssuesInTx(ctx, tx, filter)
Defensive patterns

Strategy: retry

Validate before calling

// caller-side pre-check
if tx == nil { return fmt.Errorf("no active transaction") }
if err := ctx.Err(); err != nil { return fmt.Errorf("context already done: %w", err) }

Try / catch

issues, err := issueops.GetStaleIssuesInTx(ctx, tx, filter)
if err != nil {
    var retryable = errors.Is(err, sql.ErrConnDone) || ctx.Err() == nil
    if retryable { /* retry with fresh tx */ }
    return fmt.Errorf("stale scan failed: %w", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/ed432e7b4ec2c42b. Report an issue: GitHub.