gastownhall/beads · error

determining wisp status for %s: %w

Error message

determining wisp status for %s: %w

What it means

uowMolWriter.isWisp caches whether an issue ID is an ephemeral 'wisp' by calling GetWisp. If GetWisp returns an error that is neither success nor sql.ErrNoRows (i.e. a real storage failure, not 'not a wisp'), it wraps it as 'determining wisp status for <id>'. This guards against misclassifying storage errors as 'not a wisp'.

Source

Thrown at cmd/bd/mol_port.go:389

}

func (w *uowMolWriter) isWisp(ctx context.Context, id string) (bool, error) {
	if w.wispIDs[id] {
		return true, nil
	}
	if w.notWispIDs[id] {
		return false, nil
	}
	_, err := w.uw.IssueUseCase().GetWisp(ctx, id)
	if err == nil {
		w.wispIDs[id] = true
		return true, nil
	}
	if errors.Is(err, sql.ErrNoRows) {
		w.notWispIDs[id] = true
		return false, nil
	}
	return false, fmt.Errorf("determining wisp status for %s: %w", id, err)
}

func (w *uowMolWriter) CreateIssue(ctx context.Context, issue *types.Issue, actor string) error {
	params := domain.CreateIssueParams{Issue: issue, ExplicitID: issue.ID, Labels: issue.Labels}
	var err error
	if issue.Ephemeral || issue.NoHistory {
		_, err = w.uw.IssueUseCase().CreateWisp(ctx, params, actor)
		if err == nil {
			w.wispIDs[issue.ID] = true
		}
	} else {
		_, err = w.uw.IssueUseCase().CreateIssue(ctx, params, actor)
		if err == nil {
			w.notWispIDs[issue.ID] = true
		}
	}
	return err
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped cause to identify the real storage error
  2. If a driver upgrade changed the not-found sentinel, map it to sql.ErrNoRows at the driver boundary (per the storage boundary policy)
  3. Check database health and retry transient failures (bd doctor)
  4. If caused by context cancellation, increase timeout or re-run the operation

Example fix

// before: driver returns custom ErrNotFound, isWisp treats it as a hard error
_, err := w.uw.IssueUseCase().GetWisp(ctx, id)
// after: normalize at the driver boundary so errors.Is(err, sql.ErrNoRows) works
if errors.Is(err, driver.ErrNotFound) {
	return nil, sql.ErrNoRows
}
Defensive patterns

Strategy: try-catch

Validate before calling

// probe wisp lookup before performing writes
if _, err := uw.IssueUseCase().GetWisp(ctx, id); err != nil && !errors.Is(err, sql.ErrNoRows) {
	return fmt.Errorf("storage unhealthy for %s: %w", id, err)
}

Type guard

func isWispLookupFailure(err error) bool {
	return err != nil && !errors.Is(err, sql.ErrNoRows) && !errors.Is(err, context.Canceled)
}

Try / catch

if err := writer.UpdateIssue(ctx, issue, actor); err != nil {
	if strings.Contains(err.Error(), "determining wisp status") {
		// underlying GetWisp failed; check storage, retry
	}
	return err
}

Prevention

When it happens

Trigger: Calling AddDependency, UpdateIssue, CloseIssue, or ClaimStepIfOpen on a uowMolWriter when the underlying GetWisp storage lookup fails with a non-not-found error: DB corruption, driver failure, context cancellation, or unexpected error types that don't map to sql.ErrNoRows.

Common situations: Storage driver returning a custom not-found error type that no longer matches sql.ErrNoRows (behavior change after driver/upgrade); locked or inaccessible Dolt database; context deadline exceeded during a long molecule operation.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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