gastownhall/beads · error

iter issues: build filter: %w

Error message

iter issues: build filter: %w

What it means

IterIssues failed while translating the caller's query string and IssueFilter into SQL WHERE clauses via issueops.BuildIssueFilterClauses. The filter could not be compiled to valid SQL, so no query is issued. This is a caller-input problem, not a storage problem.

Source

Thrown at internal/storage/dolt/iter_issues.go:45

	"github.com/steveyegge/beads/internal/storage"
	"github.com/steveyegge/beads/internal/storage/issueops"
	"github.com/steveyegge/beads/internal/storage/sqlbuild"
	"github.com/steveyegge/beads/internal/types"
)

// IterIssues returns issues matching the filter from the `issues` table.
//
// The path queries only the issues table (wisps are returned separately via
// IterWisps). The slice path SearchIssues merges both for backward
// compatibility — that merge needs a seen-set keyed by ID across the full
// issues result set, so it stays separate from this issues-only iterator.
func (s *DoltStore) IterIssues(ctx context.Context, query string, filter types.IssueFilter) (storage.Iter[types.Issue], error) {
	if s.closed.Load() {
		return nil, ErrStoreClosed
	}
	whereClauses, args, err := issueops.BuildIssueFilterClauses(query, filter, issueops.IssuesFilterTables)
	if err != nil {
		return nil, fmt.Errorf("iter issues: build filter: %w", err)
	}
	whereSQL := ""
	if len(whereClauses) > 0 {
		whereSQL = "WHERE " + strings.Join(whereClauses, " AND ")
	}
	limitSQL := ""
	if filter.Limit > 0 {
		limitSQL = fmt.Sprintf(" LIMIT %d", filter.Limit)
	}

	//nolint:gosec // G201: whereSQL contains column comparisons with ?, limitSQL is a safe integer
	q := fmt.Sprintf(`SELECT %s FROM issues %s %s ORDER BY priority ASC, created_at DESC, id ASC%s`,
		issueops.IssueSelectColumns, sqlbuild.LeaseJoin("issues"), whereSQL, limitSQL)

	var issues []*types.Issue
	txErr := s.withReadTx(ctx, func(tx *sql.Tx) error {
		rows, err := tx.QueryContext(ctx, q, args...)
		if err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error to identify the offending filter field or query clause
  2. Simplify the IssueFilter to a minimal known-good case (e.g. status only) and add fields back one at a time
  3. Validate query/filter syntax against the supported filter grammar for your beads version
  4. Upgrade beads if the filter uses features newer than your binary

Example fix

// before: unsupported operator
filter := types.IssueFilter{Assignee: &f, Labels: []string{"x"}, Status: "in:open,closed"}

// after: use supported fields/operators
filter := types.IssueFilter{Assignee: &f, Status: "open"}
Defensive patterns

Strategy: validation

Validate before calling

// Validate filter fields before calling IterIssues
if filter.Status != "" && !isValidStatus(filter.Status) {
    return fmt.Errorf("unsupported status filter: %q", filter.Status)
}

Try / catch

iter, err := store.IterIssues(ctx, query, filter)
if err != nil {
    if strings.Contains(err.Error(), "build filter") {
        // caller-input problem: fix filter/query, not storage
        return fmt.Errorf("invalid issue filter: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling DoltStore.IterIssues(ctx, query, filter) with a filter containing an unsupported field/operator, a malformed query expression, or a value that fails type conversion inside BuildIssueFilterClauses.

Common situations: Passing free-text query syntax the filter builder doesn't understand; using a filter field added in a newer beads version with an older storage layer; programmatically constructed filters with invalid values (empty operator, bad date format).

Related errors


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