{"record":{"id":"70b298b6b1d9afd1","repo":"toon-format/toon","slug":"unmatched-brace-in-field-list","errorCode":null,"errorMessage":"Unmatched brace in field list","messagePattern":"Unmatched brace in field list","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/toon/src/decode/parser.ts","lineNumber":239,"sourceCode":"  return entries.map((entry) => {\n    const trimmedEntry = trimSpaces(entry)\n    if (!trimmedEntry) {\n      throw new SyntaxError('Empty field name in field list')\n    }\n\n    const groupStart = findUnquotedChar(trimmedEntry, OPEN_BRACE)\n    if (groupStart === -1) {\n      return { name: parseStringLiteral(trimmedEntry) }\n    }\n\n    const namePart = trimSpaces(trimmedEntry.slice(0, groupStart))\n    if (!namePart) {\n      throw new SyntaxError('Missing field name before nested field group')\n    }\n\n    const groupEnd = findMatchingBrace(trimmedEntry, groupStart)\n    if (groupEnd === -1) {\n      throw new SyntaxError('Unmatched brace in field list')\n    }\n    if (groupEnd !== trimmedEntry.length - 1) {\n      throw new SyntaxError('Unexpected content after nested field group')\n    }\n\n    const children = parseFieldEntries(trimmedEntry.slice(groupStart + 1, groupEnd), delimiter)\n    return { name: parseStringLiteral(namePart), children }\n  })\n}\n\n/**\n * Splits a field list on the active delimiter at brace depth zero,\n * respecting quoted names and escape sequences.\n */\nfunction splitFieldEntries(content: string, delimiter: Delimiter): string[] {\n  const entries: string[] = []\n  let entryBuffer = ''\n  let inQuotes = false","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/toon-format/toon/blob/604eac266e35166bed6f5b9e3bd586e9c60a9330/packages/toon/src/decode/parser.ts#L221-L257","documentation":"A nested field group's opening brace `{` in a field-list entry had no matching closing `}` (findMatchingBrace returned -1). TOON nested field syntax must be balanced, so an unclosed group makes the header unparseable and the parser throws SyntaxError.","triggerScenarios":"Decoding input where a field-list entry contains `{` without a matching `}` — e.g. `users[2]{user{name,age}:` or `items[1]{a{b}:` — reached through parseFieldEntries from parseArrayHeaderLine.","commonSituations":"Hand-edited headers where a closing brace was deleted; truncated lines from copy-paste; template output that dropped the final `}` due to string formatting bugs.","solutions":["Add the missing closing `}` so the nested group is balanced, e.g. `user{name,age}`","Count braces in the header line to verify balance before decoding","Fix the generator/template so each `{` emits its matching `}`","Round-trip a known-good document through toon.encode to compare header syntax"],"exampleFix":"// before\nusers[2]{user{name,age}:\n// after\nusers[2]{user{name,age}}:","handlingStrategy":"try-catch","validationCode":"function bracesBalanced(headerLine) {\n  let n = 0\n  for (const ch of headerLine) {\n    if (ch === '{') n++\n    if (ch === '}') n--\n    if (n < 0) return false\n  }\n  return n === 0\n}","typeGuard":null,"tryCatchPattern":"try {\n  const value = toon.decode(text)\n} catch (err) {\n  if (err instanceof SyntaxError && err.message === 'Unmatched brace in field list') {\n    // highlight header line; check brace balance before retry\n  } else throw err\n}","preventionTips":["Verify { } balance in every header line before decoding","Avoid manual edits to nested field headers; re-encode instead","Use an editor with bracket matching when editing TOON by hand"],"tags":["syntax","toon","nested-fields","parser"],"backgroundTag":"unbalanced-braces","analyzedSha":"604eac266e35166bed6f5b9e3bd586e9c60a9330","analyzedAt":"2026-08-31T11:09:25.043Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}