gastownhall/beads · error
affected by batch delete: %w
Error message
affected by batch delete: %w
What it means
DeleteResolvedSetInTx aborts when AffectedByDeletionInTx — which computes which remaining issues would become blocked or orphaned by this deletion — fails. The wrapped error comes from that blocked-state analysis query set. This runs in dry-run mode and before actual deletes, so nothing has been removed yet when it fires.
Source
Thrown at internal/storage/issueops/delete.go:285
_ = rows.Close()
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate inbound dependencies from %s: %w", depTable, err)
}
}
}
result.DependenciesCount = depsCount
result.LabelsCount = labelsCount
result.EventsCount = eventsCount
result.DeletedCount = len(set.RegularIDs) + len(set.WispIDs)
if dryRun {
return result, nil
}
affectedIssues, affectedWisps, aerr := AffectedByDeletionInTx(ctx, tx, set.RegularIDs, set.WispIDs)
if aerr != nil {
return nil, fmt.Errorf("affected by batch delete: %w", aerr)
}
// Resolve WHICH regular ids this delete actually removes before the batched
// DELETE runs: afterwards the rows are gone, and RowsAffected reports a
// count, not a set. A journal record for an id that was already absent would
// tell a consumer to drop a bead this transaction never touched.
journaledDeletes, err := journalableDeletesInTx(ctx, tx, "issues", set.RegularIDs)
if err != nil {
return nil, err
}
// Edges are journaled before the rows go, while their source snapshots can
// still be read.
if err := RecordDependencyRemovalsForIssuesInTx(ctx, tx, set.All); err != nil {
return nil, fmt.Errorf("journal dependency removals for batch delete: %w", err)
}
for _, id := range set.WispIDs {
if err := deleteIssueRowInTx(ctx, tx, id, true); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the error for the specific query that failed inside the analysis.
- Verify the backend implements the full driver interface including blocked-state analysis.
- Retry on a fresh transaction; safe because nothing was deleted yet.
- Run schema migrations if blocked-state tables are missing.
Example fix
// before
res, err := store.DeleteInTx(ctx, tx, ids)
// after
res, err := store.DeleteInTx(ctx, tx, ids)
if err != nil {
return fmt.Errorf("no rows deleted (analysis failed first): %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
// blocked-state analysis is part of the driver surface; verify support first
if !driver.Supports(ctx, driver.FeatureBlockedStateAnalysis) {
return errors.New("backend does not support blocked-state analysis required for delete")
} Try / catch
res, err := store.DeleteInTx(ctx, tx, ids)
if err != nil && strings.Contains(err.Error(), "affected by batch delete") {
return fmt.Errorf("pre-delete analysis failed, nothing was deleted (safe to retry): %w", err)
} Prevention
- Use backends passing the conformance deleter contract.
- Run schema migrations before deleting.
- Remember this fires before any mutation — a failure here never leaves partial deletes.
When it happens
Trigger: Calling DeleteIssuesInTx/DeleteInTx when the blocked-state analysis queries fail: missing blocked-state tables, connection failure, context cancellation, or a driver that can't execute the affected-set SQL.
Common situations: Alternative/embedded drivers lacking the SQL surface AffectedByDeletionInTx needs; schema drift removing blocked-state structures; timeouts on repos with huge dependency graphs.
Related errors
- count wisp dependencies: %w
- count labels: %w
- count events: %w
- journal dependency removals for batch delete: %w
- failed to create digest issue: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/563bc948d04892ca.
Report an issue: GitHub.