{"record":{"id":"40ae36c8c32d119d","repo":"gastownhall/beads","slug":"expected-digit-at-position-d","errorCode":null,"errorMessage":"expected digit at position %d","messagePattern":"expected digit at position (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/query/lexer.go","lineNumber":253,"sourceCode":"// they can stand in unquoted on the value side of comparisons like\n// \"label=1-alpha\". Signed forms (\"-3-foo\") still error — the user has\n// to quote them.\nfunc (l *Lexer) readNumberOrDuration(startPos int) (Token, error) {\n\tvar sb strings.Builder\n\n\t// Handle optional sign\n\tr := l.next()\n\thadSign := r == '-' || r == '+'\n\tif hadSign {\n\t\tsb.WriteRune(r)\n\t\tr = l.next()\n\t}\n\n\t// Must have at least one digit\n\tif !unicode.IsDigit(r) {\n\t\tl.backup()\n\t\t// This might be a minus in front of an identifier, which is invalid\n\t\treturn Token{}, fmt.Errorf(\"expected digit at position %d\", l.pos)\n\t}\n\tsb.WriteRune(r)\n\n\t// Read remaining digits\n\tfor {\n\t\tr = l.next()\n\t\tif !unicode.IsDigit(r) {\n\t\t\tbreak\n\t\t}\n\t\tsb.WriteRune(r)\n\t}\n\n\t// Check for duration suffix. Only commit to a duration when the suffix\n\t// stands alone — if more identifier characters follow (e.g. \"7day\"),\n\t// fall through to the identifier-fallback below.\n\tif r != 0 && isDurationSuffix(r) && !isIdentChar(l.peek()) {\n\t\tsb.WriteRune(r)\n\t\treturn Token{Type: TokenDuration, Value: sb.String(), Pos: startPos}, nil","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/query/lexer.go#L235-L271","documentation":"A token starting with '-' or '+' was not followed by a digit. readNumberOrDuration (internal/query/lexer.go:253) only treats sign-led tokens as numbers/durations, so '-' or '+' followed by an identifier character (e.g. `-alpha`) is rejected. Unsigned digit-led runs with identifier continuations are tolerated (re-lexed as identifiers), but signed forms must be numeric.","triggerScenarios":"query.Parse with a sign-led non-numeric value: `label=-alpha`, `status=-new`, `priority=+high`, `assignee=-me`, or a negated bare word like `NOT -foo=1`.","commonSituations":"Users writing shell-style `-flag` prefixes inside queries; values that legitimately begin with '-' (hyphenated labels like `-experimental`) supplied unquoted; minus used to mean exclusion (`-label=foo`) as in other search DSLs.","solutions":["Quote sign-led values: `label=\"-alpha\"` (comment in lexer.go confirms signed forms must be quoted).","If you meant exclusion, there is no `-` exclusion operator — use `NOT label=foo`.","If the value is hyphenated but starts with a digit (e.g. `1-alpha`), it is already accepted unquoted; only leading signs need quoting.","Check the reported position: it points just past the sign, where a digit was expected."],"exampleFix":"// before\nquery.Parse(\"label=-alpha\")\n// after\nquery.Parse(\"label=\\\"-alpha\\\"\")","handlingStrategy":"validation","validationCode":"// Quote sign-led values before parsing\nfunc quoteSignedValues(q string) string {\n    return regexp.MustCompile(`(=|!=|<|<=|>|>=)\\s*([+-][A-Za-z_][\\w./:-]*)`).ReplaceAllString(q, \"$1\\\"$2\\\"\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := query.Parse(q); err != nil && strings.Contains(err.Error(), \"expected digit\") {\n    return fmt.Errorf(\"values starting with - or + must be quoted: %w\", err)\n}","preventionTips":["Quote any value that starts with '-' or '+'","Remember unsigned digit-led values (1-alpha, 42d-sla) are fine unquoted","Use NOT field=value for exclusion — there is no leading-minus exclusion syntax","Run the sign-quoting sanitizer on user-supplied queries"],"tags":["query-language","lexer","number-parsing"],"backgroundTag":"query-syntax-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}