{"record":{"id":"c673793272b91087","repo":"kovidgoyal/kitty","slug":"extra-backslash-at-end-of-input","errorCode":null,"errorMessage":"Extra backslash at end of input","messagePattern":"Extra backslash at end of input","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"tools/utils/shlex/shlex.go","lineNumber":102,"sourceCode":"\tconst escape_char = '\\\\'\n\tfor self.src_pos < self.src_sz {\n\t\tch := self.src[self.src_pos]\n\t\tself.src_pos++\n\t\tswitch self.state {\n\t\tcase lex_normal:\n\t\t\tswitch ch {\n\t\t\tcase ' ', '\\n', '\\r', '\\t':\n\t\t\tcase string_with_escapes_delim:\n\t\t\t\tself.state = string_with_escapes\n\t\t\t\tself.start_word()\n\t\t\tcase string_without_escapes_delim:\n\t\t\t\tself.state = string_without_escapes\n\t\t\t\tself.start_word()\n\t\t\tcase escape_char:\n\t\t\t\tself.start_word()\n\t\t\t\tif !self.write_escaped_ch() {\n\t\t\t\t\tans.Trailer = \"\\\\\"\n\t\t\t\t\tans.Err = fmt.Errorf(\"Extra backslash at end of input\")\n\t\t\t\t\tans.Pos = self.word_start\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tself.state = word\n\t\t\tdefault:\n\t\t\t\tself.state = word\n\t\t\t\tself.start_word()\n\t\t\t\tself.write_ch(ch)\n\t\t\t}\n\t\tcase word:\n\t\t\tswitch ch {\n\t\t\tcase ' ', '\\n', '\\r', '\\t':\n\t\t\t\tself.state = lex_normal\n\t\t\t\tif self.buf.Len() > 0 {\n\t\t\t\t\treturn self.get_word()\n\t\t\t\t}\n\t\t\tcase string_with_escapes_delim:\n\t\t\t\tself.state = string_with_escapes","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/tools/utils/shlex/shlex.go#L84-L120","documentation":"The shlex lexer encountered a backslash as the last character of input while in word-start/normal state, so there is no character following the escape. It records a Trailer of \"\\\\\" and this error on the LexResult instead of silently dropping the escape.","triggerScenarios":"Calling Split/SplitForCompletion on input ending in an unquoted trailing backslash, e.g. `Split(\"echo foo\\\\\")`. The escape_char case in state 'starting a word' finds no next rune via write_escaped_ch().","commonSituations":"User-supplied shell command lines pasted with a trailing backslash (line-continuation artifact), or paths on Windows ending in '\\' passed to a Unix-style splitter; completion code splitting partial input where the user just typed a backslash.","solutions":["Strip a single trailing backslash (line continuation) before splitting: strings.TrimSuffix(s, \"\\\\\").","Escape it as a doubled backslash if a literal backslash is intended.","In completion mode, check ans.Trailer == \"\\\\\" and treat it as in-progress input rather than an error.","Reject the input early with a regexp like /\\\\$/."],"exampleFix":"// before\nparts, err := shlex.Split(userInput)\n// after\nuserInput = strings.TrimSuffix(userInput, \"\\\\\")\nparts, err := shlex.Split(userInput)","handlingStrategy":"validation","validationCode":"if strings.HasSuffix(s, \"\\\\\") && !strings.HasSuffix(s, \"\\\\\\\\\") {\n    s = strings.TrimSuffix(s, \"\\\\\")\n}","typeGuard":"func hasBalancedEscapes(s string) bool {\n    return !strings.HasSuffix(s, \"\\\\\")\n}","tryCatchPattern":"words, ans := shlex.Split(s)\nif ans.Err != nil && strings.Contains(ans.Err.Error(), \"Extra backslash\") {\n    s = strings.TrimSuffix(s, \"\\\\\")\n    words, ans = shlex.Split(s)\n}","preventionTips":["Trim trailing line-continuation backslashes before splitting.","Double backslashes meant literally.","For completion, branch on ans.Trailer == \"\\\\\" as 'still typing'."],"tags":["go","shlex","shell-parsing","escaping"],"backgroundTag":"unterminated-shell-token","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}