evanw/esbuild · error · LexerPanic
Unterminated string literal
Error message
Unterminated string literal
What it means
This is a JavaScript/TypeScript lexer error indicating that a string literal (single-quote, double-quote, or template literal) was not properly terminated before the lexer reached the end of file (code point -1) or, for non-template strings, a bare newline character. The error is added as a RangeError and the lexer panics with LexerPanic to abort parsing.
Source
Thrown at internal/js_lexer/js_lexer.go:1483
stringLiteral:
for {
switch lexer.codePoint {
case '\\':
needsSlowPath = true
lexer.step()
// Handle Windows CRLF
if lexer.codePoint == '\r' && lexer.json != JSON {
lexer.step()
if lexer.codePoint == '\n' {
lexer.step()
}
continue
}
case -1: // This indicates the end of the file
lexer.addRangeError(logger.Range{Loc: logger.Loc{Start: int32(lexer.end)}}, "Unterminated string literal")
panic(LexerPanic{})
case '\r':
if quote != '`' {
lexer.addRangeError(logger.Range{Loc: logger.Loc{Start: int32(lexer.end)}}, "Unterminated string literal")
panic(LexerPanic{})
}
// Template literals require newline normalization
needsSlowPath = true
case '\n':
if quote != '`' {
lexer.addRangeError(logger.Range{Loc: logger.Loc{Start: int32(lexer.end)}}, "Unterminated string literal")
panic(LexerPanic{})
}
case '$':
if quote == '`' {View on GitHub (pinned to f6058f8364)
Solutions
- Add the missing closing quote character (' or " or `) to terminate the string
- For multi-line strings, use a backtick template literal or concatenate with '+'
- Check for stray newline characters inside the string by examining the source around the error location
- If the file appears truncated, verify the file is complete and not cut off
Example fix
// before const greeting = "hello world; // after const greeting = "hello world";
Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate source files for unterminated string literals
function checkUnterminatedStrings(source) {
for (let i = 0; i < source.length; i++) {
const ch = source[i]
if (ch === '"' || ch === "'") {
// scan forward for closing quote
let j = i + 1
while (j < source.length && source[j] !== ch) {
if (source[j] === '\\') j++ // skip escaped char
if (source[j] === '\n' || source[j] === '\r') {
return { unterminated: true, line: source.substring(0, i).split('\n').length }
}
j++
}
if (j >= source.length) {
return { unterminated: true, line: source.substring(0, i).split('\n').length }
}
i = j
}
}
return { unterminated: false }
} Try / catch
try {
const result = await esbuild.transform(source, { loader: 'ts' })
} catch (e) {
if (e.message?.includes('Unterminated string literal')) {
const loc = e.errors?.[0]?.location
console.error(`Syntax error at ${loc?.file}:${loc?.line}:${loc?.column}: add the missing quote`)
}
throw e
} Prevention
- Use a code editor with syntax highlighting to catch unclosed strings
- Run a linter (ESLint, Biome) before bundling to catch syntax errors
- For multi-line strings, use template literals (backticks) instead of quotes
- Check for stray newlines or missing escape characters in string content
When it happens
Trigger: A source file contains an unclosed string literal such as const x = "hello at the end of the file without a closing quote, or a single/double-quoted string that contains a raw newline character (not allowed in JS/TS).
Common situations: Missing closing quote due to a typo, a string accidentally spanning a newline in non-template-literal context, an escaped character that was meant to include the closing quote, or corrupted/truncated source files.
Related errors
- Unicode escape sequence is out of range
- Should be string or array of strings
- Should be string or array of strings
- The service was stopped
- [%s] %q is missing a filter
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/609f482084ebcd29.
Report an issue: GitHub.