gastownhall/beads · error

resolve existing wisps for batch delete: %w

Error message

resolve existing wisps for batch delete: %w

What it means

This error wraps failures from issueops.ExistingIssueIDsInTableInTx, which resolves which of the requested wisp IDs actually exist in the wisps table before the DELETE runs. The transaction must know the real deleted set so delete-journal records are only written for wisps it actually removes (RowsAffected gives a count, not a set). If this lookup fails, the batch aborts and rolls back.

Source

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

	}
	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)

	//nolint:gosec // G201: inClause contains only ? markers
	result, err := tx.ExecContext(ctx,
		fmt.Sprintf("DELETE FROM wisps WHERE id IN (%s)", inClause),
		args...)
	if err != nil {
		return 0, fmt.Errorf("failed to batch delete wisps: %w", err)
	}
	rowsAffected, _ := result.RowsAffected()

View on GitHub (pinned to 71377f2769)

Solutions

  1. Confirm the wisps table exists and is intact (run the tool's doctor/check or dolt schema inspect); re-run migrations if needed
  2. Retry after restoring Dolt connectivity; already-collected wisps being absent is routine and handled, only the query itself failing raises this
  3. Address the wrapped driver error (timeout, lock, cancelled ctx) reported in the error chain
  4. Ensure the caller's context is not cancelled prematurely (increase timeout, check signal handling)
Defensive patterns

Strategy: retry

Validate before calling

// Go: pre-check reachability; existence of individual IDs is handled internally
if err := ctx.Err(); err != nil { return err }
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("dolt unreachable: %w", err) }

Try / catch

_, err := store.DeleteWisps(ctx, ids)
if err != nil && strings.Contains(err.Error(), "resolve existing wisps") {
    // transient lookup failure: retry once after backoff
    time.Sleep(time.Second)
    _, err = store.DeleteWisps(ctx, ids)
}
return err

Prevention

When it happens

Trigger: DeleteWisps/GC delete path where the SELECT used by ExistingIssueIDsInTableInTx against the wisps table errors: lost Dolt connection, cancelled context, or a missing/corrupt wisps table.

Common situations: Database file locked or server unavailable during GC; running bd against a repo whose schema predates the wisps table; transient network failure between agent and a remote Dolt server.

Related errors


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