gastownhall/beads · error

get labels from %s: %w

Error message

get labels from %s: %w

What it means

Raised when the batched label lookup in getLabelsFromTable fails to execute its query against the label table, wrapped as "get labels from <table>: <err>". IDs are chunked (queryBatchSize=200) with IN (...) placeholders, so errors come from the driver: missing table, lock, connection, or malformed placeholders/args.

Source

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

	}

	return nil
}

//nolint:gosec // G201: labelTable is "labels" or "wisp_labels" (hardcoded by callers).
func (r *issueSQLRepositoryImpl) getLabelsFromTable(ctx context.Context, labelTable string, ids []string) (map[string][]string, error) {
	result := make(map[string][]string)
	for start := 0; start < len(ids); start += queryBatchSize {
		end := start + queryBatchSize
		if end > len(ids) {
			end = len(ids)
		}
		placeholders, args := buildInPlaceholders(ids[start:end])
		rows, err := r.runner.QueryContext(ctx, fmt.Sprintf(
			`SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label`,
			labelTable, placeholders), args...)
		if err != nil {
			return nil, fmt.Errorf("get labels from %s: %w", labelTable, err)
		}
		for rows.Next() {
			var issueID, label string
			if err := rows.Scan(&issueID, &label); err != nil {
				_ = rows.Close()
				return nil, fmt.Errorf("get labels: scan: %w", err)
			}
			result[issueID] = append(result[issueID], label)
		}
		_ = rows.Close()
		if err := rows.Err(); err != nil {
			return nil, fmt.Errorf("get labels: rows: %w", err)
		}
	}
	return result, nil
}

//nolint:gosec // G201: depTable is "dependencies" or "wisp_dependencies" (hardcoded by callers).

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap to identify the driver error (no such table vs locked vs too many parameters).
  2. Run `bd doctor` / migrations to create the label table with the expected schema.
  3. If hitting a parameter limit, ensure you are on a current bd version that batches IDs (200 per batch).
  4. Clear competing locks and retry; verify DB file permissions and disk space.

Example fix

// before
// old bd version builds one giant IN(...) -> "too many SQL variables"
// after
upgrade bd to a version batching label lookups (queryBatchSize = 200)
// or repair schema: 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)
}
if len(issueIDs) == 0 { return nil } // skip empty label lookups

Type guard

func isLabelLookupError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "get labels from ")
}

Try / catch

page, err := store.Search(ctx, q, filter)
if err != nil && isLabelLookupError(err) {
	if strings.Contains(err.Error(), "too many") {
		// driver parameter cap hit; upgrade bd (batched lookups) or chunk IDs
	}
	return fmt.Errorf("label lookup failed: %w", err)
}

Prevention

When it happens

Trigger: Search or fetchIssuesByIDs hydration issues that have labels; the SELECT ... WHERE issue_id IN (...) fails because the label table doesn't exist, the DB is locked, the connection drops, or the batch exceeds driver limits.

Common situations: Legacy databases without the labels table; schema renamed between bd versions; extremely large ID batches against drivers with IN-clause limits (SQLite variable caps); concurrent write locks.

Related errors


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