gastownhall/beads · error

affected by batched wisp delete: %w

Error message

affected by batched wisp delete: %w

What it means

This error wraps failures from issueops.AffectedByDeletionInTx, the pre-delete step of the batched wisp delete transaction that computes which issues and wisps would be affected (e.g. via dependency edges) by removing the given wisp IDs. The batch transaction needs this set to recompute is_blocked afterward, so if the affected-set query fails, the whole batch rolls back rather than deleting wisps with stale block status. It is wrapped so the original SQL/transaction cause is preserved in the chain.

Source

Thrown at internal/storage/dolt/wisps.go:436

	return totalDeleted, nil
}

// deleteWispBatchTx deletes one batch of wisps inside its own transaction.
// Keeping each transaction to ≤200 wisps (6 DELETE statements) ensures it
// completes well within Dolt's 10 s write timeout.
func (s *DoltStore) deleteWispBatchTx(ctx context.Context, ids []string) (int, error) {
	tx, err := s.db.BeginTx(ctx, nil)
	if err != nil {
		return 0, fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer func() { _ = tx.Rollback() }()

	clearJournalScope := s.scopeEventsJournalTransaction(tx)
	defer clearJournalScope()

	affectedIssues, affectedWisps, aerr := issueops.AffectedByDeletionInTx(ctx, tx, nil, ids)
	if aerr != nil {
		return 0, fmt.Errorf("affected by batched wisp delete: %w", aerr)
	}

	// Resolve WHICH wisps this batch actually removes before the DELETE runs:
	// afterwards they are gone, and RowsAffected reports a count, not a set.
	// GC hands this path ids it scanned earlier, so an already-collected wisp is
	// a routine case, and a phantom delete record would tell a consumer to drop
	// a bead this transaction never touched.
	deletedIDs, err := issueops.ExistingIssueIDsInTableInTx(ctx, tx, "wisps", ids)
	if err != nil {
		return 0, fmt.Errorf("resolve existing wisps for batch delete: %w", err)
	}
	// Edges are journaled before the rows go, while their source snapshots can
	// still be read.
	if err := issueops.RecordDependencyRemovalsForIssuesInTx(ctx, tx, deletedIDs); err != nil {
		return 0, fmt.Errorf("journal dependency removals for batched wisp delete: %w", err)
	}

	inClause, args := doltBuildSQLInClause(ids)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check Dolt server health/connectivity and retry the delete operation once the database is reachable
  2. Verify the schema contains the tables referenced by AffectedByDeletionInTx (wisps, dependencies/issues); run schema migrations
  3. Inspect the wrapped cause (%w) in logs for the underlying driver error and address it specifically (timeout, cancelled context, corrupt table)
  4. If ctx timeouts are the cause, increase the caller's timeout or reduce batch pressure

Example fix

// before
ids := allScannedIDs // thousands of IDs, one long-running GC pass, one ctx timeout
err := store.DeleteWisps(ctx, ids)
// after
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
err := store.DeleteWisps(ctx, ids) // batched internally at 200; healthy connection + complete schema
if err != nil { log.Printf("batch wisp delete failed: %v", err) }
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: check connectivity and context before the call
if err := ctx.Err(); err != nil { return fmt.Errorf("context already cancelled: %w", err) }
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("dolt unreachable: %w", err) }

Try / catch

deleted, err := store.DeleteWisps(ctx, ids)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
        // reschedule GC with a longer deadline
    } else if strings.Contains(err.Error(), "affected by batched wisp delete") {
        var driverErr *driverError // inspect wrapped cause via errors.As
        log.Printf("affected-set lookup failed: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling DeleteWisps (directly or via GC) when the SELECT behind AffectedByDeletionInTx fails: Dolt connection dropped or timed out, the transaction was cancelled via ctx, or the wisps/dependencies tables are missing or corrupted so the query errors.

Common situations: A GC run over a large ID set races with a Dolt server restart or network blip; running against a database where a migration didn't create the dependencies table; ctx cancellation when a caller's timeout expires mid-batch; SQLite-vs-Dolt schema drift in a partially-upgraded repository.

Related errors


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