gastownhall/beads · error
unexpected token %q at position %d (expected end of query)
Error message
unexpected token %q at position %d (expected end of query)
What it means
After successfully parsing an expression, Parse (internal/query/parser.go:119) checks that the next token is EOF. A leftover token means trailing content that the grammar cannot attach to any node — a complete expression was parsed, but the string continues with junk. The message echoes the offending token value, position, and the expectation.
Source
Thrown at internal/query/parser.go:119
}
// Parse parses the query string and returns the root AST node.
func (p *Parser) Parse() (Node, error) {
if err := p.advance(); err != nil {
return nil, err
}
if p.current.Type == TokenEOF {
return nil, fmt.Errorf("empty query")
}
node, err := p.parseOr()
if err != nil {
return nil, err
}
if p.current.Type != TokenEOF {
return nil, fmt.Errorf("unexpected token %q at position %d (expected end of query)", p.current.Value, p.current.Pos)
}
return node, nil
}
// advance moves to the next token.
func (p *Parser) advance() error {
if p.peeked != nil {
p.current = *p.peeked
p.peeked = nil
return nil
}
tok, err := p.lexer.NextToken()
if err != nil {
return err
}
p.current = tok
return nilView on GitHub (pinned to 71377f2769)
Solutions
- Insert an explicit boolean operator between conditions: `status=open AND priority>1`.
- Remove the stray trailing token (often an extra ')' or ',').
- Balance parentheses so the whole expression is consumed before EOF.
- Wrap multi-word values in quotes so they lex as one TokenString instead of several tokens: `title="bug report"`.
Example fix
// before
query.Parse("status=open priority>1")
// after
query.Parse("status=open AND priority>1") Defensive patterns
Strategy: validation
Validate before calling
// Join adjacent comparisons with AND before parsing (simple heuristic)
func joinConditions(q string) string {
return regexp.MustCompile(`(\)|\w|")\s+(?:(?i)(and|or|not)\b)@!`).ReplaceAllString(q, "$1 ") // validate manually instead
} Try / catch
node, err := query.Parse(q)
if err != nil && strings.Contains(err.Error(), "expected end of query") {
return fmt.Errorf("trailing content after a complete expression; join conditions with AND/OR: %w", err)
} Prevention
- Always write explicit AND/OR between every pair of conditions
- Wrap the whole query in quotes at the shell level so nothing is dropped or split
- Remove stray ')' or ',' characters after editing saved queries
- Quote multi-word values so they form a single token
When it happens
Trigger: query.Parse with trailing junk after a valid expression: `status=open status != closed` (two comparisons with no AND/OR), `status=open)` (stray closing paren), `status=open , priority>1` (comma outside a list context), or `NOT` alone followed by nothing then more content that terminates an expression early.
Common situations: Users writing two conditions space-separated without AND (`priority<2 status=open`) as in other search DSLs; an unbalanced `)` that terminates the group early leaving leftovers; stray commas copied from list-syntax examples.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- empty query
- expected ')' at position %d, got %s
- expected field name 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/b6a77107fbad1a90.
Report an issue: GitHub.