kovidgoyal/kitty · warning
Unterminated string at end of input
Error message
Unterminated string at end of input
What it means
The shlex lexer reached end of input while still inside a single- or double-quoted string; the closing quote never appeared. The accumulated partial word is exposed as ans.Trailer for completion use.
Source
Thrown at tools/utils/shlex/shlex.go:161
self.state = word
case escape_char:
self.write_escaped_ch()
default:
self.write_ch(ch)
}
}
}
switch self.state {
case word:
self.state = lex_normal
if self.buf.Len() > 0 {
return self.get_word()
}
case string_with_escapes, string_without_escapes:
self.state = lex_normal
ans.Trailer = self.buf.String()
ans.Pos = self.word_start
ans.Err = fmt.Errorf("Unterminated string at end of input")
return
case lex_normal:
}
return
}
// Split partitions a string into a slice of strings.
func Split(s string) (ans []string, err error) {
l := NewLexer(s)
var word Word
for {
word = l.Next()
if word.Err != nil {
return ans, word.Err
}
if word.Value == "" {
breakView on GitHub (pinned to 6d5d0c4406)
Solutions
- Balance the quotes before splitting (append the missing matching quote).
- In completion flows, detect ans.Err for unterminated string and use ans.Trailer to continue reading input.
- Validate with a quote-parity check or regexp before calling Split.
- Prefer exec.Command argument slices over building shell strings when possible.
Example fix
// before
words, err := shlex.Split(cmdline)
// after
words, ans := shlex.SplitForCompletion(cmdline)
if ans.Err != nil { // unbalanced quote
cmdline += `"` // or wait for more input in a REPL
words, _ = shlex.Split(cmdline)
} Defensive patterns
Strategy: fallback
Validate before calling
q := "'"
if strings.Count(s, "'")%2 == 1 || strings.Count(s, "\"")%2 == 1 {
s += q // naive balance; prefer rejecting
} Type guard
func quotesBalanced(s string) bool {
return strings.Count(s, "'")%2 == 0 && strings.Count(s, `"`)%2 == 0
} Try / catch
words, ans := shlex.SplitForCompletion(line)
if ans.Err != nil && strings.Contains(ans.Err.Error(), "Unterminated string") {
words = append(words, ans.Trailer) // use partial word and continue reading
} Prevention
- Use SplitForCompletion (not Split) for partial user input.
- Continue reading input when ans.Trailer is set and a quote is open.
- Prefer exec.Command slices over shell strings.
When it happens
Trigger: Split("echo 'hello world") or Split("git commit -m \"wip") — any unbalanced quote. Hit from the string_with_escapes/string_without_escapes states at EOF.
Common situations: User typing interactively (completion splits partial input), quotes spanning truncated log lines, or nested-quote mistakes when building shell commands programmatically.
Related errors
- Extra backslash at end of input
- The menu entry name in {val} must end with a double quote
- Too much piped data
- Canceled by user
- Execute command
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/fbb099569c5412d4.
Report an issue: GitHub.