kovidgoyal/kitty · warning

Extra backslash at end of input

Error message

Extra backslash at end of input

What it means

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.

Source

Thrown at tools/utils/shlex/shlex.go:102

	const escape_char = '\\'
	for self.src_pos < self.src_sz {
		ch := self.src[self.src_pos]
		self.src_pos++
		switch self.state {
		case lex_normal:
			switch ch {
			case ' ', '\n', '\r', '\t':
			case string_with_escapes_delim:
				self.state = string_with_escapes
				self.start_word()
			case string_without_escapes_delim:
				self.state = string_without_escapes
				self.start_word()
			case escape_char:
				self.start_word()
				if !self.write_escaped_ch() {
					ans.Trailer = "\\"
					ans.Err = fmt.Errorf("Extra backslash at end of input")
					ans.Pos = self.word_start
					return
				}
				self.state = word
			default:
				self.state = word
				self.start_word()
				self.write_ch(ch)
			}
		case word:
			switch ch {
			case ' ', '\n', '\r', '\t':
				self.state = lex_normal
				if self.buf.Len() > 0 {
					return self.get_word()
				}
			case string_with_escapes_delim:
				self.state = string_with_escapes

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Strip a single trailing backslash (line continuation) before splitting: strings.TrimSuffix(s, "\\").
  2. Escape it as a doubled backslash if a literal backslash is intended.
  3. In completion mode, check ans.Trailer == "\\" and treat it as in-progress input rather than an error.
  4. Reject the input early with a regexp like /\\$/.

Example fix

// before
parts, err := shlex.Split(userInput)
// after
userInput = strings.TrimSuffix(userInput, "\\")
parts, err := shlex.Split(userInput)
Defensive patterns

Strategy: validation

Validate before calling

if strings.HasSuffix(s, "\\") && !strings.HasSuffix(s, "\\\\") {
    s = strings.TrimSuffix(s, "\\")
}

Type guard

func hasBalancedEscapes(s string) bool {
    return !strings.HasSuffix(s, "\\")
}

Try / catch

words, ans := shlex.Split(s)
if ans.Err != nil && strings.Contains(ans.Err.Error(), "Extra backslash") {
    s = strings.TrimSuffix(s, "\\")
    words, ans = shlex.Split(s)
}

Prevention

When it happens

Trigger: 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().

Common situations: 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.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/c673793272b91087. Report an issue: GitHub.