gastownhall/beads · error

hydrate neighbors (wisps): %w

Error message

hydrate neighbors (wisps): %w

What it means

Wraps a failure from issueRepo.GetByIDs against the wisp table (IssueTableOpts{UseWispsTable:true}) during neighbor hydration in collectConnectedIssues. A missing wisp table is tolerated (dberrors.IsTableNotExist), so this error only fires for genuine storage failures on the wisp plane — e.g. a real query error while reading wisp_neighbor rows.

Source

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

	if len(neighbors) == 0 {
		return out, isWisp, nil
	}
	ids := make([]string, 0, len(neighbors))
	for id := range neighbors {
		ids = append(ids, id)
	}

	fromIssues, err := u.issueRepo.GetByIDs(ctx, ids, IssueTableOpts{})
	if err != nil {
		return nil, nil, fmt.Errorf("hydrate neighbors (issues): %w", err)
	}
	for _, iss := range fromIssues {
		out[iss.ID] = iss
	}
	fromWisps, err := u.issueRepo.GetByIDs(ctx, ids, IssueTableOpts{UseWispsTable: true})
	if err != nil && !dberrors.IsTableNotExist(err) {
		return nil, nil, fmt.Errorf("hydrate neighbors (wisps): %w", err)
	}
	for _, iss := range fromWisps {
		out[iss.ID] = iss
		isWisp[iss.ID] = true
	}
	return out, isWisp, nil
}

func (u *issueUseCaseImpl) rewriteTextReferences(
	ctx context.Context, deletedIDs []string,
	connected map[string]*types.Issue, isWisp map[string]bool, actor string,
) (int, error) {
	touched := make(map[string]bool)
	for _, id := range deletedIDs {
		pattern := `(^|[^A-Za-z0-9_-])(` + regexp.QuoteMeta(id) + `)($|[^A-Za-z0-9_-])`
		re := regexp.MustCompile(pattern)
		replacement := `$1[deleted:` + id + `]$3`
		for connID, conn := range connected {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause; if it is a table-missing error, verify dberrors.IsTableNotExist handling covers your driver's error shape.
  2. Check database connectivity/locks and retry the delete.
  3. Rerun with a fresh context to rule out cancellation from an upstream request.
  4. If the wisp table is corrupted, repair or recreate the wisp plane (bd doctor) before deleting.
  5. Confirm your storage driver version supports the wisps table schema in use.
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm store health before delete
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("store unreachable: %w", err) }

Type guard

func isHydrateWispsErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "hydrate neighbors (wisps)")
}

Try / catch

err := usecase.DeleteMany(ctx, ids)
if err != nil && strings.Contains(err.Error(), "hydrate neighbors (wisps)") {
    cause := errors.Unwrap(err)
    if !dberrors.IsTableNotExist(cause) {
        return fmt.Errorf("wisp hydration failed: %w", cause) // retry or escalate
    }
}

Prevention

When it happens

Trigger: deleteMany/previewDelete with dependency-connected neighbors where the wisps table exists but the bulk GetByIDs on it fails: driver error, connection loss, context cancellation, or malformed query results.

Common situations: Mixed durable/wisp stores where the wisp table is present but corrupted; transient Dolt server errors during delete; canceled long deletes; driver version mismatch causing query incompatibility on the wisp table.

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/e4845fd4b02b4152. Report an issue: GitHub.