gastownhall/beads · error
classify child-count target in wisps: %w
Error message
classify child-count target in wisps: %w
What it means
Beads throws this when the second classification probe 'SELECT 1 FROM wisps WHERE id = ?' fails with something other than 'no rows'. A missing wisps table is tolerated (wisps are optional; the code falls back to depends_on_issue_id), but any other driver error aborts the close-policy classification.
Source
Thrown at internal/storage/issueops/close.go:283
// preferring a durable row when an id is present in both tables.
func dependencyTargetColumnForIDInTx(ctx context.Context, tx DBTX, id string) (string, error) {
var found int
err := tx.QueryRowContext(ctx, "SELECT 1 FROM issues WHERE id = ?", id).Scan(&found)
if err == nil {
return "depends_on_issue_id", nil
}
if !errors.Is(err, sql.ErrNoRows) {
return "", fmt.Errorf("classify child-count target in issues: %w", err)
}
err = tx.QueryRowContext(ctx, "SELECT 1 FROM wisps WHERE id = ?", id).Scan(&found)
if err == nil {
return "depends_on_wisp_id", nil
}
if optionalBlockedTable("wisps") && isTableNotExistError(err) {
return "depends_on_issue_id", nil
}
if !errors.Is(err, sql.ErrNoRows) {
return "", fmt.Errorf("classify child-count target in wisps: %w", err)
}
return "depends_on_issue_id", nil
}
// isClosedInTx reports whether the issue (or wisp) identified by id exists and
// is already closed. It probes the issues table then the optional wisps table,
// mirroring IsBlockedInTx's table order.
//
//nolint:gosec // G201: table is a hardcoded "issues" or "wisps".
func isClosedInTx(ctx context.Context, tx DBTX, id string) (closed bool, targetColumn string, found bool, err error) {
for _, target := range []struct {
table string
column string
}{
{table: "issues", column: "depends_on_issue_id"},
{table: "wisps", column: "depends_on_wisp_id"},
} {
var status stringView on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped driver error after 'classify child-count target in wisps:'
- Retry the close if the failure was transient (connection, lock, timeout)
- Re-run wisp table migrations or drop/recreate the optional wisp schema if it is corrupt
- Confirm the DB user has SELECT on wisps if your deployment enables wisps
Defensive patterns
Strategy: try-catch
Validate before calling
// Optional: verify wisp schema is healthy before close operations
var one int
err := db.QueryRow("SELECT 1 FROM wisps LIMIT 1").Scan(&one)
wispBroken := err != nil && !dberrors.IsTableNotExist(err)
if wispBroken {
return fmt.Errorf("wisps table exists but unreadable — run migrations: %w", err)
} Try / catch
count, err := countOpenChildrenInTx(ctx, tx, id)
if err != nil {
if dberrors.IsTableNotExist(err) {
// wisps optional and missing is tolerated upstream; only surface other errors
log.Printf("wisp probe failed for %s: %v", id, err)
} else if dberrors.IsTransient(err) {
return retryClose(ctx, id)
} else {
return err
}
} Prevention
- Create wisps and wisp_dependencies in one atomic migration; never half-provision
- If wisps are disabled, ensure the optional-table gate (sqlbuild.OptionalWispTable) matches your deployment
- Grant SELECT on wisps to the application DB user when wisps are enabled
- Retry close operations after transient database errors
When it happens
Trigger: countOpenChildrenInTx → dependencyTargetColumnForIDInTx when the id is absent from issues and the wisps probe fails: connection loss, lock timeout, permission error, corrupt/partial wisp schema (wisps table exists but unreadable), or context cancellation.
Common situations: Wisp tables half-created by an interrupted migration; database unavailable between the two probes; revoked privileges on wisps; context deadline during the wisps lookup.
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
- count open wisp children for %s: %w
- search union with counts (wisps): %w
- search union with counts (hydrate wisps): %w
- count wisp labels: %w
- count wisp events: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/7ffdb68d3049df50.
Report an issue: GitHub.