gastownhall/beads · error

wisp id set: probe: %w

Error message

wisp id set: probe: %w

What it means

WispIDSetInTx first probes whether the wisps table is empty or missing before batch-querying ids; this error wraps a failure of that probe itself. A missing table is treated as empty (not an error), so reaching this error means a real probe failure: connection problems, permission denied, or an unexpected SQL error from the existence check.

Source

Thrown at internal/storage/issueops/wisp_routing.go:73

// WispIDSetInTx returns the subset of ids that are currently-active wisps
// within the tx. The set is consistent for the tx's lifetime (Dolt MVCC).
// Intended for hot-path partitioning where a batch of IDs must be split
// into wisps vs permanents; one scoped query amortized over the batch
// replaces N per-ID IsActiveWispInTx calls without paying for a full
// wisps-table scan when callers have a small batch against a large
// wisps table.
//
// Returns an empty set when ids is empty; never issues a query.
//
//nolint:gosec // G201: query uses placeholder-only interpolation
func WispIDSetInTx(ctx context.Context, tx DBTX, ids []string) (map[string]struct{}, error) {
	set := make(map[string]struct{})
	if len(ids) == 0 {
		return set, nil
	}
	if empty, err := wispsTableEmptyOrMissingInTx(ctx, tx); err != nil {
		return nil, fmt.Errorf("wisp id set: probe: %w", err)
	} else if empty {
		return set, nil
	}
	for start := 0; start < len(ids); start += queryBatchSize {
		end := start + queryBatchSize
		if end > len(ids) {
			end = len(ids)
		}
		batch := ids[start:end]
		placeholders := make([]string, len(batch))
		args := make([]any, len(batch))
		for i, id := range batch {
			placeholders[i] = "?"
			args[i] = id
		}
		q := fmt.Sprintf("SELECT id FROM wisps WHERE id IN (%s)", strings.Join(placeholders, ","))
		rows, err := tx.QueryContext(ctx, q, args...)
		if err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped probe error to distinguish permissions vs connection vs SQL incompatibility.
  2. Grant the database user SELECT privileges on the wisps table (or create it via migration).
  3. Verify the backend supports the probe query (MySQL/Dolt-compatible driver required).
  4. Retry after the connection is restored.

Example fix

// before: restricted user
GRANT SELECT ON db.issues TO 'beads'@'%';
// after
GRANT SELECT ON db.* TO 'beads'@'%';
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the user can read the wisps table
var one int
err := db.QueryRow("SELECT 1 FROM wisps LIMIT 1").Scan(&one)
if err != nil {
    var myErr *mysql.MySQLError
    if errors.As(err, &myErr) && (myErr.Number == 1142 || myErr.Number == 1146) {
        return fmt.Errorf("wisps table inaccessible/missing: grant SELECT or run migration")
    }
}

Try / catch

if err != nil {
    var myErr *mysql.MySQLError
    if errors.As(err, &myErr) && myErr.Number == 1142 {
        return fmt.Errorf("insufficient privileges on wisps table: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling any of WispIDSetInTx's callers (ReconcileChildCounters, DeleteInTx, GetIssuesByIDsInTx, ExecuteAddDependencies) when the wisps-table emptiness probe fails due to a broken connection, insufficient privileges, or probe-query incompatibility with the backend.

Common situations: User lacks SELECT/SHOW permission on the wisps table; connection dropped; non-MySQL backend where the probe's SQL (information_schema lookups or similar) does not exist.

Related errors


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