gastownhall/beads · error
count wisp-only dependent records: %w
Error message
count wisp-only dependent records: %w
What it means
This error wraps the failure of the COUNT(*) query that counts wisp-only inbound dependency edges (rows in wisp_dependencies whose id is not present in the durable dependencies table for the same target). Called from CountDependentRecordsInTx via countWispDependentsNotInDurableInTx.
Source
Thrown at internal/storage/issueops/dependency_queries.go:410
func countWispDependentsNotInDurableInTx(ctx context.Context, tx DBTX, targetID, depType string) (int, error) {
wispWhere := depTargetEqualsOr()
durableWhere := depTargetEqualsOr()
args := []any{targetID, targetID, targetID}
if depType != "" {
wispWhere += " AND type = ?"
args = append(args, depType)
}
args = append(args, targetID, targetID, targetID)
if depType != "" {
durableWhere += " AND type = ?"
args = append(args, depType)
}
query := fmt.Sprintf(
"SELECT COUNT(*) FROM wisp_dependencies WHERE %s AND id NOT IN (SELECT id FROM dependencies WHERE %s)",
wispWhere, durableWhere)
var n int
if err := tx.QueryRowContext(ctx, query, args...).Scan(&n); err != nil {
return 0, fmt.Errorf("count wisp-only dependent records: %w", err)
}
return n, nil
}
//nolint:gosec // G201: depTable is a hardcoded constant; targetID/depType are bound as parameters.
func countDependentRecordsFromTable(ctx context.Context, tx DBTX, depTable, targetID, depType string) (int, error) {
query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s", depTable, depTargetEqualsOr())
args := []any{targetID, targetID, targetID}
if depType != "" {
query += " AND type = ?"
args = append(args, depType)
}
var n int
if err := tx.QueryRowContext(ctx, query, args...).Scan(&n); err != nil {
return 0, fmt.Errorf("count dependent records from %s: %w", depTable, err)
}
return n, nil
}View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped driver error; if it is 'table not found' for wisp_dependencies, run migrations (note the caller may already treat it as optional and return the durable count).
- Verify both dependencies and wisp_dependencies exist and are readable by the DB user.
- Retry if the error is transient (connection/context).
- Check grants for the database user on both tables.
- Confirm count semantics: absence of wisp table means durable count is the whole answer.
Example fix
// before: user lacks SELECT on wisp table GRANT SELECT ON db.wisp_dependencies TO 'beads'@'%'; // after: grant covers both dependency tables GRANT SELECT ON db.* TO 'beads'@'%';
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure wisp_dependencies exists and is readable before counting
if err := tx.QueryRowContext(ctx, "SELECT 1 FROM wisp_dependencies LIMIT 1").Scan(&one); err != nil {
if !isTableNotExistError(err) { return fmt.Errorf("wisp count will fail: %w", err) }
} Type guard
func wispTableReadable(ctx context.Context, tx DBTX) bool {
var one int
return tx.QueryRowContext(ctx, "SELECT 1 FROM wisp_dependencies LIMIT 1").Scan(&one) == nil
} Try / catch
n, err := CountDependentRecordsInTx(ctx, tx, targetID, depType)
if err != nil {
if isTableNotExistError(err) { n = durableOnlyCount /* wisp absent: durable count is the answer */ }
else if isTransientNetError(err) { /* retry with backoff */ }
else { return err }
} Prevention
- Grant SELECT on both dependency tables to the app user
- Run migrations so wisp_dependencies exists before reads
- Note that a missing wisp table degrades to durable-only counts by design
- Watch connection stability for count-heavy workloads
When it happens
Trigger: Calling CountDependentRecordsInTx when wisp_dependencies or dependencies table is missing (though absence is usually tolerated upstream via isTableNotExistError), the context is canceled, or the connection fails while running the NOT IN subquery.
Common situations: Unmigrated database lacking wisp_dependencies; transient connection errors during count; permission issues preventing reads on either table.
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 dependent records from %s: %w
- set repo mtime: %w
- clear repo mtime: %w
- failed to claim issue: %w
- get dependent records from %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9968f673110844a4.
Report an issue: GitHub.