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

  1. 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).
  2. Verify both dependencies and wisp_dependencies exist and are readable by the DB user.
  3. Retry if the error is transient (connection/context).
  4. Check grants for the database user on both tables.
  5. 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

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


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