toon-format/toon · error · SyntaxError

Unexpected content after nested field group

Error message

Unexpected content after nested field group

What it means

A nested field group in a field-list entry was properly closed, but the closing `}` was not the last character of the trimmed entry — extra content followed it (e.g. `user{name}extra`). TOON requires the group to terminate the entry, so trailing content is rejected to avoid silently misparsing.

Source

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

      throw new SyntaxError('Empty field name in field list')
    }

    const groupStart = findUnquotedChar(trimmedEntry, OPEN_BRACE)
    if (groupStart === -1) {
      return { name: parseStringLiteral(trimmedEntry) }
    }

    const namePart = trimSpaces(trimmedEntry.slice(0, groupStart))
    if (!namePart) {
      throw new SyntaxError('Missing field name before nested field group')
    }

    const groupEnd = findMatchingBrace(trimmedEntry, groupStart)
    if (groupEnd === -1) {
      throw new SyntaxError('Unmatched brace in field list')
    }
    if (groupEnd !== trimmedEntry.length - 1) {
      throw new SyntaxError('Unexpected content after nested field group')
    }

    const children = parseFieldEntries(trimmedEntry.slice(groupStart + 1, groupEnd), delimiter)
    return { name: parseStringLiteral(namePart), children }
  })
}

/**
 * Splits a field list on the active delimiter at brace depth zero,
 * respecting quoted names and escape sequences.
 */
function splitFieldEntries(content: string, delimiter: Delimiter): string[] {
  const entries: string[] = []
  let entryBuffer = ''
  let inQuotes = false
  let braceDepth = 0
  let i = 0

View on GitHub (pinned to 604eac266e)

Solutions

  1. Move any sibling fields after the group into a separate delimiter-separated entry, e.g. `{user{name},age}` not `{user{name}age}`
  2. Remove stray characters after the closing `}`
  3. Fix the generator so nested groups are always the last part of their entry
  4. Compare against output from toon.encode for correct field-list ordering

Example fix

// before
users[2]{user{name}age}:
// after
users[2]{user{name},age}:
Defensive patterns

Strategy: try-catch

Validate before calling

function noContentAfterGroups(toonText) {
  const m = toonText.match(/\[\d+\]\{(.*)\}\s*:$/m)
  if (!m) return true
  return !/\}[^,;}]/.test(m[1])
}

Try / catch

try {
  const value = toon.decode(text)
} catch (err) {
  if (err instanceof SyntaxError && err.message.includes('after nested field group')) {
    // move trailing text into its own delimiter-separated entry
  } else throw err
}

Prevention

When it happens

Trigger: Decoding a field list where text follows a nested group's closing brace, like `items[2]{user{name}x,age}:` — parseFieldEntries checks groupEnd !== trimmedEntry.length - 1 and throws. Reached via parseArrayHeaderLine.

Common situations: Hand-edited headers appending delimiters or text after a nested group instead of before it; generators that concatenate fields in the wrong order (group first, then siblings inside the same entry instead of after the comma).

Related errors


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