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 + deleteBatchSize

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the error to see the driver cause (unknown column/table vs connection reset).
  2. Verify schema/migrations have run so labels matches the expected shape.
  3. Retry the operation with a fresh transaction; check for long-held locks on labels.
  4. 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

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


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