{"record":{"id":"9af33881015a894d","repo":"toon-format/toon","slug":"empty-field-name-in-field-list","errorCode":null,"errorMessage":"Empty field name in field list","messagePattern":"Empty field name in field list","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/toon/src/decode/parser.ts","lineNumber":224,"sourceCode":"\n  return { length: Number.parseInt(content, 10), delimiter, keyed }\n}\n\n/**\n * Parses the content of a field list into field entries, recursively\n * descending into nested field groups (`field{sub1,sub2}`).\n *\n * @remarks\n * Throws on empty segments, empty names, unmatched braces, and content\n * after a nested group's closing brace; callers decide strict fallthrough.\n */\nexport function parseFieldEntries(fieldsContent: string, delimiter: Delimiter): FieldNode[] {\n  const entries = splitFieldEntries(fieldsContent, delimiter)\n\n  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')","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/toon-format/toon/blob/604eac266e35166bed6f5b9e3bd586e9c60a9330/packages/toon/src/decode/parser.ts#L206-L242","documentation":"A field list in an array header (e.g. `{a,b}`) contained an empty entry between delimiters, such as `{a,,b}` or `{,a}`. TOON requires every comma-separated entry in the field list to be a non-empty field name, so the parser rejects the header.","triggerScenarios":"Decoding input whose array header field list has a doubled delimiter or a leading/trailing delimiter — e.g. `items[2]{a,,b}:`, `items[1]{,a}:`, or a trailing comma `items[2]{a,b,}:`. Reached via parseFieldEntries from parseArrayHeaderLine.","commonSituations":"Manual editing of TOON files that leaves a stray comma; generators that join field names with a delimiter and include an empty name (e.g. objects with an empty/whitespace key); template-based output where a column was removed but its delimiter wasn't.","solutions":["Remove the empty entry: delete extra delimiters so every entry has a name, e.g. `{a,b}` not `{a,,b}`","Rename fields with empty or whitespace-only keys to non-empty names before encoding","Fix the encoder/generator to skip or rename empty keys instead of emitting them","Re-export the data (e.g. from CSV/JSON) ensuring all column headers are non-empty"],"exampleFix":"// before\nitems[3]{a,,b}:\n// after\nitems[3]{a,b}:","handlingStrategy":"try-catch","validationCode":"function hasEmptyFieldEntries(toonText) {\n  const m = toonText.match(/\\[\\d+\\]\\{([^}]*)\\}/)\n  if (!m) return false\n  return m[1].split(/[,;\\t]/).some(f => f.trim() === '')\n}","typeGuard":null,"tryCatchPattern":"try {\n  const value = toon.decode(text)\n} catch (err) {\n  if (err instanceof SyntaxError && err.message === 'Empty field name in field list') {\n    // report offending header line for fixing\n  } else throw err\n}","preventionTips":["Ensure all object keys are non-empty before encoding","Join field names without emitting empty entries","After editing TOON by hand, scan field lists for doubled/edge delimiters"],"tags":["syntax","toon","field-list","parser"],"backgroundTag":"invalid-array-header","analyzedSha":"604eac266e35166bed6f5b9e3bd586e9c60a9330","analyzedAt":"2026-08-31T11:09:25.043Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}