gastownhall/beads · error

hydrate labels: %w

Error message

hydrate labels: %w

What it means

Raised inside hydrateIssues when getLabelsFromTable fails fetching label rows for the hydrated issue IDs from the given label table. The error wraps the underlying query/scan failure as "hydrate labels: <err>" and aborts hydration of the result page.

Source

Thrown at internal/storage/domain/db/issue_search.go:337

	sortRowsGoSide(ids, func(id string) string { return id }, filter.SortBy, filter.SortDesc)
	return finishWindow(ids, window)
}

func (r *issueSQLRepositoryImpl) hydrateIssues(ctx context.Context, issues []*types.Issue, tables filterTables, includeDeps bool, skipLabels bool) error {
	if len(issues) == 0 {
		return nil
	}

	ids := make([]string, len(issues))
	for i, issue := range issues {
		ids[i] = issue.ID
	}

	if !skipLabels {
		labelMap, err := r.getLabelsFromTable(ctx, tables.Labels, ids)
		if err != nil {
			return fmt.Errorf("hydrate labels: %w", err)
		}
		for _, issue := range issues {
			if labels, ok := labelMap[issue.ID]; ok {
				issue.Labels = labels
			}
		}
	}

	if includeDeps {
		depMap, err := r.getDependencyRecordsFromTable(ctx, tables.Dependencies, ids)
		if err != nil {
			return fmt.Errorf("hydrate dependencies: %w", err)
		}
		for _, issue := range issues {
			if deps, ok := depMap[issue.ID]; ok {
				issue.Dependencies = deps
			}
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap to distinguish query failure from scan failure.
  2. Run `bd doctor` / migrations so the labels table exists with the expected columns.
  3. Repair or remove malformed label rows (NULL issue_id/label).
  4. Retry after clearing locks from other bd processes; use SkipWisps if wisp side tables are intentionally absent.

Example fix

// before
// wisp_labels table missing -> "no such table: wisp_labels"
page, err := store.Search(ctx, q, types.IssueFilter{Ephemeral: &t})
// after
page, err := store.Search(ctx, q, types.IssueFilter{SkipWisps: true})
// or run migrations: bd doctor / bd migrate
Defensive patterns

Strategy: validation

Validate before calling

if err := bd.CheckTables(dbPath, "labels"); err != nil {
	return fmt.Errorf("run `bd migrate`: %w", err)
}

Type guard

func isLabelHydrateError(err error) bool {
	return err != nil && strings.HasPrefix(err.Error(), "hydrate labels: ")
}

Try / catch

page, err := store.Search(ctx, q, filter)
if err != nil && isLabelHydrateError(err) {
	if name, ok := dberrors.MissingTableName(err); ok && sqlbuild.OptionalWispTable(name) {
		filter.SkipWisps = true // retry issues-plane only
		page, err = store.Search(ctx, q, filter)
	}
}

Prevention

When it happens

Trigger: Batched `SELECT issue_id, label FROM <labels> WHERE issue_id IN (...)` fails: the labels (or wisp_labels) table is missing, the DB is locked, the connection drops, or a label row cannot be scanned (NULL/non-string value).

Common situations: Old database created before the labels table existed; migration skipped; corrupt label rows added by external tooling; concurrent bd write lock stalling reads; wisp searches hitting an absent optional wisp_labels table.

Related errors


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