toon-format/toon · error · SyntaxError

Unterminated string: missing closing quote

Error message

Unterminated string: missing closing quote

What it means

A primitive token in the TOON input started with a double quote, indicating a quoted string, but findClosingQuote could not locate an unescaped closing quote before end-of-token. The parser throws SyntaxError rather than treating the rest of the line as string content, since the token boundaries are ambiguous.

Source

Thrown at packages/toon/src/decode/parser.ts:475

      return null
  }

  if (isNumericLiteral(trimmedToken)) {
    const parsedNumber = Number.parseFloat(trimmedToken)
    return Object.is(parsedNumber, -0) ? 0 : parsedNumber
  }

  return trimmedToken
}

export function parseStringLiteral(token: string): string {
  const trimmedToken = trimSpaces(token)

  if (trimmedToken.startsWith(DOUBLE_QUOTE)) {
    const closingQuoteIndex = findClosingQuote(trimmedToken, 0)

    if (closingQuoteIndex === -1) {
      throw new SyntaxError('Unterminated string: missing closing quote')
    }

    if (closingQuoteIndex !== trimmedToken.length - 1) {
      throw new SyntaxError('Unexpected characters after closing quote')
    }

    const content = trimmedToken.slice(1, closingQuoteIndex)
    return unescapeString(content)
  }

  return trimmedToken
}

export function parseUnquotedKey(content: string, start: number): { key: string, end: number } {
  // A raw scan would cut `a "b:c" d: 1` at the quoted colon and split the key in two.
  const colonIndex = findUnquotedChar(content, COLON, start)

  if (colonIndex === -1) {

View on GitHub (pinned to 604eac266e)

Solutions

  1. Add the missing closing quote to the string token, e.g. `name: "hello"`
  2. Check for unescaped inner quotes — escape them as `\"` so the parser doesn't end the string early
  3. Avoid manual quoting: emit values via toon.encode which quotes/escapes correctly
  4. Check the file wasn't truncated mid-line (inspect the end of the file/line)

Example fix

// before
greeting: "hello
// after
greeting: "hello"
Defensive patterns

Strategy: try-catch

Validate before calling

function linesHaveClosedQuotes(text) {
  return text.split('\n').every(line => {
    let n = 0, esc = false
    for (const ch of line) {
      if (esc) { esc = false; continue }
      if (ch === '\\') { esc = true; continue }
      if (ch === '"') n++
    }
    return n % 2 === 0
  })
}

Try / catch

try {
  const value = toon.decode(text)
} catch (err) {
  if (err instanceof SyntaxError && err.message === 'Unterminated string: missing closing quote') {
    // point at the offending line; fix quoting before retry
  } else throw err
}

Prevention

When it happens

Trigger: Decoding TOON text containing an opening `"` with no closing `"` on the same token/line — e.g. `name: "hello` or an array row cell `"unterminated,x`. Reached via parseStringLiteral from parsePrimitiveToken, parseArrayHeaderLine, or parseFieldEntries.

Common situations: Hand-written TOON where the closing quote was forgotten; text containing characters that were escaped incorrectly so an escaped quote `\"` accidentally consumed the real closing quote; files truncated mid-line; content pasted from sources that strip trailing quotes.

Related errors


AI-assisted analysis of toon-format/toon@604eac266e (2026-08-31). Data as JSON: /api/errors/66a4c1207c54628a. Report an issue: GitHub.