gastownhall/beads · error
unexpected character %q at position %d
Error message
unexpected character %q at position %d
What it means
The lexer read a rune that cannot start any token: it is not an operator, quote, digit/sign, or identifier start (letter or underscore). NextToken (internal/query/lexer.go:191) rejects it with the character in %q form and its byte position. Note the lexer advances by single bytes (rune(l.input[l.pos])), so the offending rune is the first byte of any unsupported character.
Source
Thrown at internal/query/lexer.go:191
return Token{Type: TokenLess, Value: "<", Pos: startPos}, nil
case '>':
if l.peek() == '=' {
l.next()
return Token{Type: TokenGreaterEq, Value: ">=", Pos: startPos}, nil
}
return Token{Type: TokenGreater, Value: ">", Pos: startPos}, nil
case '"', '\'':
return l.readString(r, startPos)
default:
if unicode.IsDigit(r) || r == '-' || r == '+' {
l.backup()
return l.readNumberOrDuration(startPos)
}
if isIdentStart(r) {
l.backup()
return l.readIdent(startPos)
}
return Token{}, fmt.Errorf("unexpected character %q at position %d", r, startPos)
}
}
// readString reads a quoted string.
func (l *Lexer) readString(quote rune, startPos int) (Token, error) {
var sb strings.Builder
for {
r := l.next()
if r == 0 {
return Token{}, fmt.Errorf("unterminated string starting at position %d", startPos)
}
if r == quote {
return Token{Type: TokenString, Value: sb.String(), Pos: startPos}, nil
}
if r == '\\' {
// Handle escape sequences
escaped := l.next()
switch escaped {View on GitHub (pinned to 71377f2769)
Solutions
- Remove or replace the offending character at the reported position (note: positions are byte offsets).
- Replace SQL-style operators: use AND/OR instead of &&/||, and drop trailing ';'.
- Quote literal values that contain special characters: `title="a&b"`.
- Replace smart quotes/dashes with plain ASCII ('"', '-').
Example fix
// before
query.Parse("status = open;")
// after
query.Parse("status = open") Defensive patterns
Strategy: validation
Validate before calling
// Reject characters the lexer cannot start a token with
var invalid = regexp.MustCompile(`[;#*@~&|`]`)
func queryHasBadChars(q string) bool {
inQuote := rune(0)
for _, r := range q {
if inQuote != 0 {
if r == inQuote { inQuote = 0 }
continue
}
if r == '"' || r == '\'' { inQuote = r; continue }
if invalid.MatchString(string(r)) { return true }
}
return false
} Try / catch
tokens, err := query.NewLexer(q).Tokenize()
if err != nil {
return fmt.Errorf("invalid query %q: %w", q, err)
} Prevention
- Drop SQL habits: no trailing semicolons, no &&/|| (use AND/OR)
- Quote any value containing special characters
- Use plain ASCII quotes and dashes; avoid smart punctuation from editors
- Sanitize queries built from user input before parsing
When it happens
Trigger: Any query.NextToken/Tokenize/Parse call where the input contains an unsupported character, e.g. `status = open;` (semicolon), `status="open" AND priority>1 #comment`, `*`, `@`, `~`, unquoted `a&b`, or stray control/non-ASCII punctuation bytes.
Common situations: Pasting queries from SQL/JS habit (trailing `;`, `&&`, `||`); comments typed into saved queries; smart quotes or curly dashes from word processors; shell metacharacters (`&`, `|`, `*`) surviving into the query string.
Related errors
- unexpected character '!' at position %d (did you mean '!=' o
- unterminated string starting at position %d
- unterminated escape sequence at position %d
- expected digit at position %d
- closed does not support %s operator
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b9f7a5247b33429a.
Report an issue: GitHub.