gastownhall/beads · error
expected field name at position %d, got %s
Error message
expected field name at position %d, got %s
What it means
parseComparison (internal/query/parser.go:236) requires the expression to begin with a TokenIdent (the field name). Any other token — a string, number, operator, '(' or EOF — triggers this error naming the token type. Effectively, a comparison cannot start with a literal or operator.
Source
Thrown at internal/query/parser.go:236
if err != nil {
return nil, err
}
if p.current.Type != TokenRParen {
return nil, fmt.Errorf("expected ')' at position %d, got %s", p.current.Pos, p.current.Type.String())
}
if err := p.advance(); err != nil {
return nil, err
}
return node, nil
}
return p.parseComparison()
}
// parseComparison parses a field comparison.
func (p *Parser) parseComparison() (Node, error) {
if p.current.Type != TokenIdent {
return nil, fmt.Errorf("expected field name at position %d, got %s", p.current.Pos, p.current.Type.String())
}
// Field names are case-insensitive, but the key suffix of a
// metadata.<key> field keeps its original case: metadata keys are
// case-sensitive JSON keys (has_metadata_key and the --metadata flag
// already preserve case), so lowercasing it would make mixed-case keys
// silently unqueryable.
field := strings.ToLower(p.current.Value)
if strings.HasPrefix(field, "metadata.") && len(field) > len("metadata.") {
field = "metadata." + p.current.Value[len("metadata."):]
}
if err := p.advance(); err != nil {
return nil, err
}
var op ComparisonOp
switch p.current.Type {
case TokenEquals:View on GitHub (pinned to 71377f2769)
Solutions
- Start the comparison with an unquoted field name (identifier): `status=open`, not `"status"=open`.
- Check KnownFields (query.KnownFields) for valid field names; a misspelled leading keyword like `AN status=open` still lexes as ident 'AN' but `AND status=open` fails here if it reaches comparison position.
- Ensure the field placeholder in generated queries is non-empty before the operator.
- Do not begin an expression with a literal; the language has no literal-to-literal comparisons.
Example fix
// before
query.Parse("\"status\"=open")
// after
query.Parse("status=open") Defensive patterns
Strategy: validation
Validate before calling
if query.KnownFields[field] {
// safe to build "field=value"
} Try / catch
if _, err := query.Parse(q); err != nil && strings.Contains(err.Error(), "expected field name") {
return fmt.Errorf("comparison must start with an unquoted field name: %w", err)
} Prevention
- Never quote field names — quote only values
- Validate field names against query.KnownFields before building the query
- Ensure generated queries have a non-empty field placeholder before the operator
- Start every expression with a field comparison, not a literal or operator
When it happens
Trigger: query.Parse where a value appears where a field is expected: `"status"=open` (quoted field name), `=open`, `7d>created`, `AND status=open`, `(status=open` reaching parseComparison without a field (e.g. `NOT ( )` -> group then comparison on ')'), or a number leading the query like `1=1`.
Common situations: Quoting field names out of habit from other query languages; strings beginning with an operator because the field name was typo'd into a keyword (e.g. starting with AND/OR/NOT followed by nothing comparable); template output where the field placeholder was empty and only the value remained.
Related errors
- empty query
- unexpected token %q at position %d (expected end of query)
- expected ')' at position %d, got %s
- expected comparison operator at position %d, got %s
- unexpected token after expression
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f5991c92b24a2fbd.
Report an issue: GitHub.