gastownhall/beads · error

delete: count labels

Error message

delete: count labels: %w

What it means

deleteMany wraps errors from labelRepo.CountAllForIDs on the regular-issues table when pre-deleting label counts are gathered for the result stats. The 'delete: count labels:' prefix marks the failure; the underlying repository error is preserved via %w. No deletion has occurred yet.

Solutions

  1. Unwrap the error to see the CountAllForIDs driver failure
  2. Verify the labels table exists and Dolt is healthy
  3. Retry the delete; deletion has not started, so it is safe
  4. If stats are non-essential, consider whether the library offers a mode that tolerates count failures

Example fix

// before
res, err := uc.DeleteIssues(ctx, ids, actor)
// after
if err != nil {
	if strings.Contains(err.Error(), "count labels") {
		log.Printf("label count failed, nothing deleted: %v", err)
	}
	return err
}
Defensive patterns

Strategy: retry

Validate before calling

if err := pingDolt(ctx); err != nil {
	return fmt.Errorf("storage unavailable, skipping delete: %w", err)
}

Try / catch

res, err := uc.DeleteIssues(ctx, ids, actor)
if err != nil && strings.Contains(err.Error(), "count labels") {
	// stats phase failed; nothing deleted — retry after health check
	return retryDelete(ctx, ids)
}

Prevention

When it happens

Trigger: DeleteIssue/DeleteIssues/DeleteWisps/DeleteWisp reaching the stats-counting phase when CountAllForIDs(ctx, regularIDs, LabelOpts{}) fails — DB error, missing labels table, or connection loss.

Common situations: Deletes with result-stat collection running against a Dolt instance with schema drift or an in-progress migration; connection pool exhaustion during bulk deletes.

Related errors


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

Appendix: source

Thrown at internal/storage/domain/issue_delete.go:125

	}
	if len(allIDs) == 0 {
		return DeleteIssuesResult{}, nil
	}

	wispIDs, regularIDs, err := u.issueRepo.PartitionWispIDs(ctx, allIDs)
	if err != nil {
		return DeleteIssuesResult{}, fmt.Errorf("delete: partition: %w", err)
	}

	depCount, err := u.countDeletedDependencies(ctx, allIDs)
	if err != nil {
		return DeleteIssuesResult{}, err
	}
	result.DependenciesCount = depCount

	labelIssue, err := u.labelRepo.CountAllForIDs(ctx, regularIDs, LabelOpts{})
	if err != nil {
		return DeleteIssuesResult{}, fmt.Errorf("delete: count labels: %w", err)
	}
	labelWisp, err := u.labelRepo.CountAllForIDs(ctx, wispIDs, LabelOpts{UseWispsTable: true})
	if err != nil {
		return DeleteIssuesResult{}, fmt.Errorf("delete: count wisp labels: %w", err)
	}
	result.LabelsCount = labelIssue + labelWisp

	evIssue, err := u.eventsRepo.CountAllForIDs(ctx, regularIDs, RecordEventOpts{})
	if err != nil {
		return DeleteIssuesResult{}, fmt.Errorf("delete: count events: %w", err)
	}
	evWisp, err := u.eventsRepo.CountAllForIDs(ctx, wispIDs, RecordEventOpts{UseWispsTable: true})
	if err != nil {
		return DeleteIssuesResult{}, fmt.Errorf("delete: count wisp events: %w", err)
	}
	result.EventsCount = evIssue + evWisp

	if params.DryRun {

View on GitHub (pinned to 71377f2769)