gastownhall/beads · error

count wisp labels

Error message

count wisp labels: %w

What it means

DeleteResolvedSetInTx aborts when it cannot count rows in the "wisp_labels" table for the wisp IDs in the resolved set. This wraps the driver-level error returned by CountRowsForIssueIDsInTx. The count contributes to the labels figure of the delete result.

Solutions

  1. Read the wrapped driver error to distinguish missing table from connection failure.
  2. Run schema migrations / ensure the backend supports wisp tables.
  3. Retry on a fresh transaction if the cause is transient.
  4. Check database server logs around the failure time.

Example fix

// before
if err != nil {
    return err
}
// after
if err != nil {
    var missing tableMissingError
    if errors.As(err, &missing) {
        return fmt.Errorf("run 'bd migrate' first: %w", err)
    }
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// confirm wisp support before calling delete on wisps
rows, err := db.QueryContext(ctx,
    `SELECT table_name FROM information_schema.tables WHERE table_name = 'wisp_labels'`)
if err != nil || !rows.Next() {
    return errors.New("wisp_labels missing: migrate schema or use a wisp-capable backend")
}

Try / catch

res, err := store.DeleteInTx(ctx, tx, ids)
if err != nil && strings.Contains(err.Error(), "wisp_labels") {
    return fmt.Errorf("backend lacks wisp support or schema is stale: %w", err)
}

Prevention

When it happens

Trigger: Calling DeleteInTx/DeleteIssuesInTx where wisp_labels is absent (pre-wisp schema or driver that doesn't create wisp tables), corrupted, or the connection fails between the labels count and this query.

Common situations: Embedded-mode or older storage backend lacking wisp_labels; Dolt schema drift after a version upgrade; transient I/O error on the database file/server.

Related errors


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

Appendix: source

Thrown at internal/storage/issueops/delete.go:226

	}

	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
		if end > len(set.All) {
			end = len(set.All)
		}
		batch := set.All[i:end]

View on GitHub (pinned to 71377f2769)