ErrLookup › Background articles › "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained
"query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained
Wrapped SQL query failures — errors like "check remaining blockers from %s: %w", "failed to query orphaned dependencies: %w", or "Cannot fetch records for <Class>" — mean a SELECT or write issued by a library against its database backend (SQLite, Dolt, MySQL) failed at the driver or server level. Developers hit these when running CLI commands, imports, doctor-style repairs, or batch reads: the fix is to read the wrapped %w cause, which almost always points to a missing or unmigrated table, a schema drift after an upgrade, a lost connection, or a context that was cancelled mid-query.
Distilled from 112 documented records across 3 repositories.
Background
This family covers errors produced not by a library's own logic but by the database layer underneath it. A Go library opens a transaction, issues a SELECT, INSERT, or UPDATE through database/sql (QueryContext, QueryRowContext, Scan), and the driver or server rejects it. The library then wraps that raw driver error with a message naming the failing operation — sometimes the table involved — and returns it to the caller. The wrap pattern ("doing X from <table>: %w") is deliberate: it tells you which step of a multi-step operation failed while preserving the underlying error via %w so the real cause (missing table, lost connection, permission denied) is one unwrap away.
The most common producing layer is a storage repository inside an application or CLI tool. In the beads issue tracker, dozens of storage methods follow this shape: import paths wrap config writes and empty-database checks ("importing config %q: %w", "checking issue count: %w"), dependency-graph reads name the exact table that failed ("get dependency counts (dependents from %s): %w", "failed to get parent-child deps from %s: %w"), and repair/doctor commands wrap their data-gathering queries ("failed to query orphaned dependencies: %w", "failed to recompute is_blocked: %w"). On the Java side, Hadoop's state store shows the same family in a different language: any SQLException from fetching router state records becomes an IOException ("Cannot fetch records for <Class>") with the SQL exception as cause.
From the caller's side, these errors are usually abort-with-rollback. Because most of the failing queries run inside transactions — imports, ID generation, recompute passes — the surrounding transaction rolls back, leaving the database in its prior state and the operation safely retryable. Several libraries exploit this deliberately: a failed beads import leaves the database still empty, so rerunning is idempotent. The error message is therefore not a corruption report in most cases; it is a pre-operation environmental failure (schema, connectivity, permissions, timing) that stopped the operation before it could change anything.
The family varies in how much detail it surfaces. Some wrappers embed the table name ("get labels for issues from %s: %w") so you immediately know which table to inspect; others echo query arguments ("events since cursor (%v, %q) issue %q: %w") to make the failing query reproducible. Some libraries also define tolerated cases: an optional table that doesn't exist (for example beads' wisp_dependencies on pre-migration databases) is skipped gracefully, so the wrapped error fires only when the table exists but is broken, or when a required table is missing. Reading the wrapped cause is essential — the same wrapper can cover a table-not-exist, a connection reset, a lock timeout, a permission denial, or a context cancellation, and the remedies differ.
Common causes
- Missing or unmigrated schema. The table the query targets doesn't exist yet — the schema was never provisioned or migrations haven't run after a binary upgrade. This is the most frequent cause across the records: missing config, metadata, labels, dependency, or wisp tables all surface as wrapped query errors. Run the tool's migrate/doctor command before retrying.
- Schema drift or mismatched versions. The table exists but its shape is wrong: a column like is_blocked, created_at, or status is absent after a version mismatch, or the binary expects columns the deployed schema lacks. Keep the client/storage versions aligned and verify the schema matches what the library version expects.
- Lost or broken connection. The database server dropped between connection setup and the query, or died mid-transaction — common with embedded or remote backends like Dolt. The operation aborts and (in transactional paths) rolls back; retrying on a fresh connection usually works once the server is healthy.
- Cancelled or timed-out context. The context passed to the query was cancelled or its deadline expired while the query ran. Large scans, big imports, and deep recursive walks (such as recursive CTE descendant queries) are the usual victims — raise the timeout or narrow the operation.
- Driver parameter or batch-size limits. Batched reads with huge IN (...) clause overflow driver placeholder limits, or large batches exceed packet limits like max_allowed_packet. Chunk the ID list into a few hundred per call.
- Lock contention or concurrent writers. Another writer holds a lock, or concurrent DDL runs while the tool is reading, producing lock timeouts or aborted reads. Avoid running migrations or bulk writes concurrently with queries, and retry after transient lock errors.
- Permissions denied. The database user lacks SELECT (or write) grants on a required table, so the server rejects the statement. Grant privileges on all of the library's tables, including optional ones like wisp_dependencies.
- Corrupt or unavailable database. The database file or server is corrupt or misbehaving — a table exists but is unreadable, or a server-side query engine bug rejects a valid query (for example, the Dolt recursive-CTE analyzer bug noted in the records). Restore from backup, re-initialize, or change engine versions.
What usually fixes it
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
Go deeper
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Documented occurrences
- importing config %q: %w (gastownhall/beads)
- check remaining blockers from %s: %w (gastownhall/beads)
- setting issue_prefix: %w (gastownhall/beads)
- read newest comment time from %s: %w (gastownhall/beads)
- failed to recompute is_blocked: %w (gastownhall/beads)
- failed to query orphaned dependencies: %w (gastownhall/beads)
- scan same-content compaction snapshots: %w (gastownhall/beads)
- get dependency counts (dependents from %s): %w (gastownhall/beads)
- get dependent records from %s: %w (gastownhall/beads)
- failed to query child-parent dependencies: %w (gastownhall/beads)
- count open wisp children for %s: %w (gastownhall/beads)
- search count %s: %w (gastownhall/beads)
- get next child ID: read counter: %w (gastownhall/beads)
- Cannot fetch records for {clazz} (apache/hadoop)
- get all dependency records from %s: %w (gastownhall/beads)
- checking issue count: %w (gastownhall/beads)
- get labels: %w (gastownhall/beads)
- get labels for issues from %s: %w (gastownhall/beads)
- query dependency targets: %w (gastownhall/beads)
- db: GetCustomTypes: query custom_types: %w (gastownhall/beads)
…and 92 more across the corpus — use search.
Honest provenance: generated on 2026-08-30 from AI-assisted analysis of the linked records. See how records are made.