gastownhall/beads · error
count labels: %w
Error message
count labels: %w
What it means
DeleteResolvedSetInTx aborts when the SELECT COUNT over the "labels" table for the regular issue IDs fails. The message wraps the underlying driver error from CountRowsForIssueIDsInTx. This count feeds the labels-count figure of the dry-run delete result.
Source
Thrown at internal/storage/issueops/delete.go:222
deletedSet := make(map[string]bool, len(set.All))
for _, id := range set.All {
deletedSet[id] = true
}
var depsCount, labelsCount, eventsCount int
var err error
if depsCount, err = CountRowsForIssueIDsInTx(ctx, tx, "dependencies", set.RegularIDs); err != nil {
return nil, fmt.Errorf("count dependencies: %w", err)
}
wispDepsCount, err := CountRowsForIssueIDsInTx(ctx, tx, "wisp_dependencies", set.WispIDs)
if err != nil {
return nil, fmt.Errorf("count wisp dependencies: %w", err)
}
depsCount += wispDepsCount
if labelsCount, err = CountRowsForIssueIDsInTx(ctx, tx, "labels", set.RegularIDs); err != nil {
return nil, fmt.Errorf("count labels: %w", err)
}
wispLabelsCount, err := CountRowsForIssueIDsInTx(ctx, tx, "wisp_labels", set.WispIDs)
if err != nil {
return nil, fmt.Errorf("count wisp labels: %w", err)
}
labelsCount += wispLabelsCount
if eventsCount, err = CountRowsForIssueIDsInTx(ctx, tx, "events", set.RegularIDs); err != nil {
return nil, fmt.Errorf("count events: %w", err)
}
wispEventsCount, err := CountRowsForIssueIDsInTx(ctx, tx, "wisp_events", set.WispIDs)
if err != nil {
return nil, fmt.Errorf("count wisp events: %w", err)
}
eventsCount += wispEventsCount
for i := 0; i < len(set.All); i += deleteBatchSize {
end := i + deleteBatchSizeView on GitHub (pinned to 71377f2769)
Solutions
- Unwrap the error to see the driver cause (unknown column/table vs connection reset).
- Verify schema/migrations have run so labels matches the expected shape.
- Retry the operation with a fresh transaction; check for long-held locks on labels.
- Increase the context timeout when deleting large issue sets.
Example fix
// before ctx := context.Background() res, err := store.DeleteIssuesInTx(ctx, tx, ids) // after ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() res, err := store.DeleteIssuesInTx(ctx, tx, ids)
Defensive patterns
Strategy: retry
Validate before calling
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("db unreachable before delete: %w", err)
}
// ensure labels table exists
var n int
if err := db.QueryRowContext(ctx, `SELECT COUNT(*) FROM labels LIMIT 1`).Scan(&n); err != nil {
return fmt.Errorf("labels table unreadable: %w", err)
} Try / catch
res, err := store.DeleteIssuesInTx(ctx, tx, ids)
if err != nil {
var derr driverError
if errors.As(err, &derr) && derr.Retryable() {
err = retryWithBackoff(3, func() error { _, err = store.DeleteIssuesInTx(ctx, tx, ids); return err })
}
if err != nil { return err }
} Prevention
- Keep schemas migrated to the beads version you run.
- Avoid very short context timeouts on bulk deletes.
- Check for lock contention from concurrent writers before large deletes.
When it happens
Trigger: Calling DeleteIssuesInTx/DeleteInTx when the labels table is missing or unreadable, the transaction's connection has died, or ctx is cancelled before this point in the count sequence (deps counts succeed, labels count fails).
Common situations: Old database schema without the expected labels table layout; Dolt server connection drop mid-transaction; permission loss on the labels table; context timeout set too short for large ID batches.
Related errors
- count wisp dependencies: %w
- count events: %w
- count wisp labels: %w
- count wisp events: %w
- count inbound dependencies from %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/e651c2bc7245e474.
Report an issue: GitHub.