{"record":{"id":"a97d821b162d1fea","repo":"gastownhall/beads","slug":"unterminated-escape-sequence-at-position-d","errorCode":null,"errorMessage":"unterminated escape sequence at position %d","messagePattern":"unterminated escape sequence at position (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/query/lexer.go","lineNumber":221,"sourceCode":"\t\tif r == quote {\n\t\t\treturn Token{Type: TokenString, Value: sb.String(), Pos: startPos}, nil\n\t\t}\n\t\tif r == '\\\\' {\n\t\t\t// Handle escape sequences\n\t\t\tescaped := l.next()\n\t\t\tswitch escaped {\n\t\t\tcase 'n':\n\t\t\t\tsb.WriteRune('\\n')\n\t\t\tcase 't':\n\t\t\t\tsb.WriteRune('\\t')\n\t\t\tcase '\\\\':\n\t\t\t\tsb.WriteRune('\\\\')\n\t\t\tcase '\"':\n\t\t\t\tsb.WriteRune('\"')\n\t\t\tcase '\\'':\n\t\t\t\tsb.WriteRune('\\'')\n\t\t\tcase 0:\n\t\t\t\treturn Token{}, fmt.Errorf(\"unterminated escape sequence at position %d\", l.pos-1)\n\t\t\tdefault:\n\t\t\t\tsb.WriteRune(escaped)\n\t\t\t}\n\t\t} else {\n\t\t\tsb.WriteRune(r)\n\t\t}\n\t}\n}\n\n// readNumberOrDuration reads a number or duration (e.g., 7d, 24h).\n//\n// Unsigned digit-led tokens that continue into identifier characters\n// (e.g. \"1-alpha\", \"42day-sla\", \"9.3.1\") are re-lexed as identifiers so\n// 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","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/query/lexer.go#L203-L239","documentation":"Inside a quoted string, a backslash started an escape sequence but the input ended immediately after the backslash (the escaped rune was 0). readString (internal/query/lexer.go:221) reports position l.pos-1, which is the position of the trailing backslash.","triggerScenarios":"query.Parse where the string's last character before EOF is a backslash: `title=\"path\\\\` (value ends with `\\\\` at EOF) or `status='open\\\\`.","commonSituations":"Windows-style path values cut off (`C:\\\\Users\\\\...` truncated); a value ending in a backslash where the author forgot the lexer requires escaping (`\\\\` for a literal backslash) and the closing quote was also lost; template-generated queries where a trailing backslash fell out of variable interpolation.","solutions":["Close the string properly: a trailing backslash before EOF is always wrong; end with the closing quote.","Escape literal backslashes by doubling them: `path=\"C:\\\\\\\\Users\"`.","If the backslash was meant literally as the final character, write `\\\\` then the quote: `value=\"end\\\\\\\\\"`.","Check shell escaping — an extra shell-level `\\\\` can consume the query's closing quote, leaving the lexer's backslash dangling at EOF."],"exampleFix":"// before\nquery.Parse(\"title=\\\"report\\\\\") // backslash then EOF\n// after\nquery.Parse(\"title=\\\"report\\\\\\\\\\\"\") // escaped backslash + closing quote","handlingStrategy":"validation","validationCode":"// Reject a string value whose final character before EOF is a dangling backslash\nfunc trailingBackslash(q string) bool {\n    return strings.HasSuffix(q, \"\\\\\") || strings.HasSuffix(q, \"\\\\'\") && !strings.HasSuffix(q, \"\\\\\\\\'\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := query.Parse(q); err != nil && strings.Contains(err.Error(), \"unterminated escape sequence\") {\n    return fmt.Errorf(\"query ends with a bare backslash; escape it as \\\\\\\\\")\n}","preventionTips":["Double every literal backslash: \\\\ for one backslash","Never end a quoted value with a single backslash","Beware double-escaping when the query passes through a shell plus a config file","Normalize Windows paths (forward slashes) before embedding in queries"],"tags":["query-language","lexer","escape-sequence"],"backgroundTag":"unterminated-string-literal","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}