gastownhall/beads · error

reading custom statuses for reference scan: %w

Error message

reading custom statuses for reference scan: %w

What it means

When ProtectReferenced is set, sweepReferencedInTx must first resolve the workspace's custom statuses via ResolveCustomStatusesDetailedInTx; a failure here is wrapped with this message. The comment in sweep.go states this is deliberately NOT best-effort: an error aborts the sweep, because silently missing a custom active status would under-protect and delete a bead a live bead still cites.

Source

Thrown at internal/storage/issueops/sweep.go:96

// transaction the candidates came off.
//
// Reading the workspace's custom statuses is required rather than best-effort:
// an error aborts the sweep, because a scan that silently missed a custom active
// status would under-protect and delete a bead a live bead still cites
// (issueops.SweepRequest.ProtectReferenced).
func sweepReferencedInTx(ctx context.Context, tx *sql.Tx, candidates []*types.Issue) (map[string]bool, error) {
	if len(candidates) == 0 {
		return nil, nil
	}
	candidateIDs := make(map[string]bool, len(candidates))
	for _, issue := range candidates {
		candidateIDs[issue.ID] = true
	}
	matcher := workapi.NewCandidateIDMatcher(candidateIDs)

	custom, err := ResolveCustomStatusesDetailedInTx(ctx, tx)
	if err != nil {
		return nil, fmt.Errorf("reading custom statuses for reference scan: %w", err)
	}
	notDone, err := SearchIssuesInTx(ctx, tx, "", workapi.BuildSweepReferenceScanFilter(custom))
	if err != nil {
		return nil, fmt.Errorf("scanning open beads for references: %w", err)
	}

	notDoneIDs := make([]string, 0, len(notDone))
	for _, issue := range notDone {
		if issue != nil {
			notDoneIDs = append(notDoneIDs, issue.ID)
		}
	}
	comments, err := GetCommentsForIssuesInTx(ctx, tx, notDoneIDs)
	if err != nil {
		return nil, fmt.Errorf("scanning open beads for references: %w", err)
	}

	referenced := make(map[string]bool)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error from ResolveCustomStatusesDetailedInTx — fix the underlying custom-status config or storage problem.
  2. Do NOT retry deletion without protection: this failure is fail-safe by design to prevent deleting referenced beads.
  3. Repair or regenerate the custom-status configuration (reset to defaults if corrupted) and re-run the sweep.
  4. Ensure the database connection and context are healthy, then retry the sweep in dry-run first.

Example fix

// before
result, err := SweepInTx(ctx, tx, req) // aborts: custom statuses unreadable
// after
if _, err := store.ResolveCustomStatusesDetailed(ctx); err != nil {
    return fmt.Errorf("fix custom statuses before sweeping: %w", err) // fail-safe
}
result, err = SweepInTx(ctx, tx, req)
Defensive patterns

Strategy: fallback

Validate before calling

// pre-resolve custom statuses so an unreadable config surfaces before sweeping
custom, err := issueops.ResolveCustomStatusesDetailed(ctx)
if err != nil { return fmt.Errorf("resolve custom statuses before sweep: %w", err) }

Try / catch

result, err := issueops.SweepInTx(ctx, tx, req)
if err != nil && strings.Contains(err.Error(), "reading custom statuses for reference scan") {
    // fail-safe: nothing was deleted; fix config, do NOT bypass protection
    return fmt.Errorf("sweep aborted to protect referenced beads: %w", err)
}

Prevention

When it happens

Trigger: ResolveCustomStatusesDetailedInTx fails due to a missing/corrupt config or statuses storage, a broken connection/tx, or a cancelled context — while running a sweep with ProtectReferenced enabled (the default protection path of `bd prune`/`bd purge`).

Common situations: A workspace with hand-edited or corrupted custom-status configuration; database unavailability mid-sweep; version migration left custom status metadata unreadable; context timeout on large workspaces.

Related errors


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