How to fix JSON trailing comma parsing errors across every runtime

Quick answer

💡Remove the comma that appears immediately before a closing brace } or bracket ]. RFC 8259 grammar defines objects and arrays without a trailing-separator production, so any strict JSON parser will reject the document at that position. Paste your JSON into a formatter to pinpoint the exact line before editing it manually.

Error symptoms

  • SyntaxError: Unexpected token } in JSON at position 47 (V8 / Node.js)
  • JSONDecodeError: Expecting property name enclosed in double quotes: line 3 column 1 (Python json.loads)
  • jq: parse error (Invalid numeric literal at EOF at line 5, column 2)
  • java.lang.RuntimeException: Expecting value: line 2 column 18 (Gson / Jackson)
  • JSON.parse returns nothing but browser DevTools shows the object fine
  • CI pipeline fails on a config file that passes in VS Code

Common causes

  • Comma left after the final property when another property was deleted
  • Template loop appends a comma after every element including the last
  • JavaScript object literal pasted directly into a JSON file
  • tsconfig.json or .eslintrc (JSONC format) fed to a strict JSON.parse call
  • Code generator emits commas before closing tokens instead of between items
  • Copy-paste from a code example that used JSON5 or JavaScript object syntax

When it happens

  • Editing a hand-crafted JSON config file and removing a field
  • Posting application/json request bodies built with string concatenation
  • Running jq in a CI pipeline against generated fixture files
  • Deserializing API responses that were generated by a non-standard serializer
  • Loading package.json, tsconfig.json, or eslintrc in a Node.js script

Example 1

A comma left after removing the 'burst' field breaks every strict parser.

Trailing comma in a nested object

❌ Wrong

{
  "service": "tooldock",
  "limits": {
    "dailyRuns": 100,
    "concurrency": 5,
  },
  "enabled": true
}

✅ Fixed

{
  "service": "tooldock",
  "limits": {
    "dailyRuns": 100,
    "concurrency": 5
  },
  "enabled": true
}

V8 throws 'SyntaxError: Unexpected token }' because after the comma the parser expects another key-value pair before the closing brace. Python's json.loads gives 'Expecting property name enclosed in double quotes: line 5 column 3'. The fix removes only the trailing comma — nothing else changes. This pattern appears most often when a developer deletes the last field in an object but forgets to clean up the separator on the line above.

Why parsers reject trailing commas

The JSON grammar defined in RFC 8259 (and its predecessor RFC 4627) describes an object as a pair of curly braces enclosing zero or more name-value pairs separated by commas. The production rule is: object = begin-object [ member *( value-separator member ) ] end-object. There is no rule that allows a value-separator immediately before end-object. Array is defined symmetrically. This means any conforming parser must reject a document where a comma precedes } or ] — it is not a parser quirk, it is required by the specification.

The V8 engine used in Node.js, Chrome, and Deno throws 'SyntaxError: Unexpected token } in JSON at position N' where N is the character offset of the closing brace. That position number is your fastest diagnostic clue — count N characters from the start of the string and you land right on the offending token. Python's json.loads raises a JSONDecodeError with 'Expecting property name enclosed in double quotes' because after a comma it expects to read a string key, not a closing brace. jq produces 'parse error (Invalid numeric literal at EOF)' in some versions because the trailing comma can confuse the token stream's lookahead.

The confusion between languages arises because JavaScript (since ES5), Python dicts, and many configuration formats allow trailing commas. TypeScript's tsconfig.json is actually JSONC (JSON with Comments), which allows trailing commas and line comments. .eslintrc files are also processed as JSONC. Prettier and VS Code's JSON language server silently accept trailing commas when the file is identified as JSONC. Developers who write tsconfig.json files every day then carry that muscle memory into contexts where the recipient is a strict RFC-compliant parser — like fetch, JSON.parse, or curl piped to jq.

A template or code-generation pattern is another common root cause. If a code generator iterates a list and appends a comma character after every element (rather than inserting a comma between adjacent elements), the last element gets a trailing comma. This mistake is easy to introduce with sprintf, string concatenation, or naive Jinja2 templates, and it appears in generated OpenAPI specs, Terraform JSON configs, and AWS CloudFormation templates that use custom tooling.

Spotting commas in nested JSON structures

Start with the exact error message. V8 gives you a character offset. Convert it to a line and column using: echo 'YOUR_JSON' | node -e "const j=require('fs').readFileSync('/dev/stdin','utf8'); const pos=47; console.log(j.substring(pos-20,pos+20));". That 40-character window will show the comma and the token that follows it. Python's JSONDecodeError already includes line and column numbers directly in the message.

For files on disk, grep is fast: grep -n ',\s*[}\]]' file.json finds lines where a comma is followed only by whitespace and then a closing token. This regex catches the most common pattern in under a second even for multi-megabyte files. For deeply nested structures the match may appear inside a string value, so confirm visually before editing.

Browser DevTools is an unreliable diagnostic tool for this specific error. Chrome's DevTools Network panel and the built-in JSON viewer parse with a lenient internal parser before display, so a document that causes JSON.parse to throw can still render as an expanded tree in DevTools. Always reproduce the failure with the same strict parser that failed in production. In Node.js: node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))" will throw with the exact error and position.

For generated JSON, the problem is usually upstream of the file. Add a validation step immediately after generation. In shell pipelines: generate_config | python3 -m json.tool > /dev/null checks well-formedness before the file reaches jq or curl. In Node.js projects, add a lint-staged hook that runs JSON.parse on staged .json files. For JSONC files (tsconfig.json, .vscode/settings.json), use the strip-json-comments npm package before feeding content to a strict parser. Do not use a regex to strip comments from JSONC — it will break on URLs and strings containing comment-like sequences.

Example 2

Appending a comma after every item instead of between items breaks strict parsers.

Template loop producing trailing comma in an array

❌ Wrong

// Broken: comma appended unconditionally
const tools = ["json-validator", "json-formatter", "json-minifier",];
const body = JSON.stringify({ tools });
// Produces: {"tools":["json-validator","json-formatter","json-minifier",]}
// JSON.parse(body) → SyntaxError
fetch('/api/config', { method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body })

✅ Fixed

// Fixed: JSON.stringify handles separators correctly
const tools = ["json-validator", "json-formatter", "json-minifier"];
const body = JSON.stringify({ tools });
// Produces: {"tools":["json-validator","json-formatter","json-minifier"]}
// JSON.parse(body) → {tools: [...]}
fetch('/api/config', { method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body })

Array literals in JavaScript allow trailing commas since ES5, but the JSON specification does not. When you pass that array through JSON.stringify the serializer actually produces valid JSON — so the bug here is in hand-rolled string building, not JavaScript arrays. The pattern becomes dangerous when developers build JSON by concatenating strings in a loop and append a comma after each iteration. Using JSON.stringify on a proper JavaScript object eliminates this class of bug entirely.

JSON5 is not JSON

tsconfig.json and .eslintrc use JSONC (JSON with Comments), not strict JSON. Feeding them to JSON.parse directly will fail — strip comments and trailing commas first using the jsonc-parser npm package.

Removing trailing commas at scale

For a single file, open it in a formatter that understands strict JSON and will highlight or auto-remove the comma. The ToolDock JSON Formatter at /tools/json-formatter will flag the exact position and produce a clean output you can copy back. Alternatively, run python3 -m json.tool --no-ensure-ascii input.json > output.json — Python's built-in JSON tool reformats strict JSON and will fail loudly on a trailing comma rather than silently dropping it.

For an entire codebase, eslint-plugin-json enforces JSON syntax rules including trailing-comma detection when you add "plugin:json/recommended" to your ESLint config. This integrates with your existing CI workflow and pre-commit hooks. Prettier with --parser json also refuses to format files with trailing commas and exits non-zero, making it suitable as a pre-commit check via lint-staged. Add these to package.json scripts: "lint:json": "eslint --ext .json .".

For JSONC files that legitimately use trailing commas (tsconfig.json, .vscode/*.json, .eslintrc.json), install strip-json-comments (npm install --save-dev strip-json-comments) and strip before parsing: const stripJsonComments = require('strip-json-comments'); const parsed = JSON.parse(stripJsonComments(fs.readFileSync('tsconfig.json', 'utf8'))). This handles both line comments (//) and trailing commas correctly without a fragile regex.

For code generators producing JSON, switch from string concatenation to a proper serializer. In Python, build a dict and call json.dumps(data, indent=2). In Node.js, build an object and call JSON.stringify(data, null, 2). In Jinja2, use the tojson filter. Serializers never produce trailing commas because they model the separator between elements, not after each element. If you cannot change the generator, add a post-processing validation step: any output that fails JSON.parse should fail the build, not silently pass corrupted data downstream.

When remediating trailing commas across a large repository, a simple shell one-liner can inventory the problem before you fix it: grep -rl ',\s*[}\]]' . --include='*.json' | grep -v node_modules | wc -l. This counts affected files without modifying them. Once you have the complete list, run them through python3 -m json.tool one by one and commit the corrections as a single chore commit so the change is easy to review and revert if needed. Treating the fix as a single atomic commit also makes it straightforward to bisect if a downstream test breaks.

When JSON5 and JSONC allow commas

JSON5 (json5.org) extends JSON to allow trailing commas in objects and arrays, single-quoted strings, unquoted keys, and comments. It is intentionally designed as a superset to ease hand-authoring. tsconfig.json, .eslintrc, .babelrc, and VS Code workspace settings files all use JSONC, which is distinct from JSON5 but also allows trailing commas and // comments. The TypeScript compiler and VS Code parse these with their own JSONC parsers, not with JSON.parse.

The failure mode happens when developers grep or programmatically read these files using JSON.parse — for example, a build script that reads tsconfig.json to find the outDir setting. The file is valid JSONC but invalid strict JSON. The correct approach is to use a JSONC-aware parser: import { parse } from 'jsonc-parser' (the npm package published by Microsoft) rather than JSON.parse. This library handles both trailing commas and comments and is the same parser VS Code uses internally.

Another edge case is JSON generated by languages with different default serialization behaviors. Ruby's JSON.generate and PHP's json_encode both produce strict JSON. But older versions of some ORM serializers or custom to_json methods in Ruby on Rails models have occasionally produced trailing commas when monkey-patched incorrectly. If you receive JSON from a third-party API and it has a trailing comma, that is a bug in their serializer. Log the raw response, report it, and apply strip-json-comments or a lenient parser only as a temporary workaround while the upstream fix is deployed.

JSON path libraries like jsonpath-plus and jq both fail on trailing commas. There is no lenient mode flag in jq — it either parses or it does not. AWS CloudFormation and Terraform JSON both use strict JSON parsers. GitHub Actions workflow YAML files do not use JSON at all, but the YAML values may contain embedded JSON strings for matrix strategies, and those embedded strings must be strict JSON. A trailing comma inside a matrix JSON string is one of the harder-to-spot sources of the error.

The regex stripping anti-pattern

A widespread but dangerous workaround is using a regular expression to remove trailing commas before parsing. The most common form looks like: jsonString.replace(/,\s*([}\]])/g, '$1'). This regex matches a comma followed by optional whitespace and a closing token and replaces it with just the closing token. It works for simple cases but fails silently in at least three real production scenarios.

First, if a JSON string value contains the characters ,} or ,] (which is valid — for example a URL like 'https://example.com/api,}extra' or a code snippet), the regex will corrupt the string value by removing the comma. Second, if the JSON contains an array of arrays or objects, the regex may match across nested structures incorrectly depending on whitespace. Third, the regex gives you no feedback when it removes something — you might strip a legitimate comma that was actually part of a bug elsewhere, producing silently wrong data.

The correct approach is to use a proper JSONC parser that understands the grammar: jsonc-parser for Node.js, or strip-json-comments followed by JSON.parse. These tools parse with a full grammar rather than pattern matching, so they handle nested structures and string values correctly. If the file is supposed to be strict JSON (not JSONC), the right fix is to fix the source — not to make the consumer lenient.

A second common mistake is relying on JSON.parse inside a try-catch and then logging the error without the position information. V8's SyntaxError has a message that includes the position, but it is easy to log only error.message and discard the context. Log the full error object or at minimum error.message, and if possible log the first 200 characters of the input string to help future debugging. Do not swallow parsing errors with a fallback to a default value — that pattern hides data corruption.

Finally, developers sometimes add overflow: visible to a parent to solve a layout problem — the CSS equivalent of using a lenient parser. In JSON's case, the equivalent mistake is switching from JSON.parse to eval() or the Function() constructor to 'parse' JavaScript object literals. That approach introduces an arbitrary code execution risk if the string comes from untrusted input. Always use a proper parser.

Linting JSON in CI pipelines

The most effective long-term prevention is catching trailing commas before they reach production, which means linting at commit time. Add eslint-plugin-json to your project and configure it in .eslintrc: { "plugins": ["json"], "rules": { "json/*": "error" } }. Then add a lint-staged entry to run ESLint on .json files during git commit. This catches trailing commas, unquoted keys, duplicate keys, and other JSON syntax errors before they enter the repository.

For projects that mix strict JSON and JSONC files, configure ESLint to apply different rules per file glob. Use "overrides" in .eslintrc to apply json/recommended only to files that must be strict JSON (package.json, *.schema.json, API fixtures) and a JSONC-aware ruleset to tsconfig.json and .vscode/*.json. This makes the distinction explicit in version control and prevents copy-paste confusion.

In CI, add python3 -m json.tool --no-ensure-ascii or jq . as a validation step for any JSON file that will be consumed by a strict parser. Both tools exit non-zero on invalid JSON and produce clear output showing the line and position of the error. This is faster than running a full Node.js linter and works in any CI environment without npm dependencies.

For teams that generate JSON programmatically (Terraform, CloudFormation, OpenAPI), add a schema validation step using ajv (npm) or jsonschema (Python) in addition to syntax validation. Schema validation catches structural errors that syntax validation misses. Run these checks in the same pre-commit hook or CI job as the syntax linter, so failures are caught early and the error message points to the source rather than a downstream consumer.

Document the distinction between strict JSON files and JSONC files in your repository's contributing guide. A single paragraph explaining that tsconfig.json is JSONC (and can have trailing commas and comments) while api-fixtures/ must be strict JSON prevents a significant category of developer confusion. Link to RFC 8259 for the authoritative specification. The specification is only 16 pages and the grammar section that forbids trailing commas is on page 5.

Quick fix checklist

  • Read the exact error message — V8 gives a character offset, Python gives line and column.
  • Run node -e "JSON.parse(require('fs').readFileSync('file.json','utf8'))" to reproduce locally.
  • Use grep -n ',\s*[}\]]' file.json to find trailing comma candidates quickly.
  • Check if the file is JSONC (tsconfig.json, .eslintrc) — those allow trailing commas by design.
  • If JSONC is being fed to JSON.parse, switch to the jsonc-parser npm package.
  • Replace string-concatenation JSON builders with JSON.stringify or json.dumps.
  • Add eslint-plugin-json to lint-staged so CI catches future trailing commas at commit time.
  • Never use a regex to strip trailing commas — use a proper JSONC-aware parser.

Frequently asked questions

Why does my JSON work in VS Code but fail in production?

VS Code's JSON language server uses a JSONC parser that accepts trailing commas and comments by default. Most production runtimes — Node.js JSON.parse, Python json.loads, jq — use strict RFC 8259 parsers that do not. The fix is to remove the trailing comma from the source file, not to make the production parser lenient.

Can JSON.parse be configured to allow trailing commas?

No. The ECMAScript specification defines JSON.parse to conform to RFC 8259, which forbids trailing commas. There is no options argument for lenient mode. If you need trailing comma support in a config file, use the jsonc-parser npm package — the same parser VS Code uses internally — which handles trailing commas and comments correctly.

Is it safe to use a regex to strip trailing commas?

No. A regex like /,\s*([}\]])/g will incorrectly match commas inside string values — for example, a URL like 'https://example.com/api,}extra' or an embedded code snippet. The regex removes that comma too, silently corrupting valid data. Use strip-json-comments (npm) or jsonc-parser instead: these parse the full grammar and handle nested structures correctly.

What character position does V8 report for a trailing comma error?

V8 reports the position of the closing brace or bracket that appears after the trailing comma, not the comma itself. So 'Unexpected token } in JSON at position 47' means character 47 is the }, and the trailing comma is one or more characters before that, possibly separated by whitespace.

Does Python's json.loads give the line number of the trailing comma?

Yes. Python's JSONDecodeError includes line number, column number, and the full document position. The error message reads: 'Expecting property name enclosed in double quotes: line 5 column 3 (char 82)'. Line 5, column 3 is where the parser expected a new key but found the closing brace instead — the comma is on the preceding line.

Why does jq report 'Invalid numeric literal' for a trailing comma?

jq's error messages for trailing commas can be misleading because the token stream lookahead works differently from standard JSON parsers. The 'Invalid numeric literal at EOF' message appears in some jq versions when the trailing comma causes the parser to enter an unexpected state. Validate the JSON with python3 -m json.tool first to get a clearer error message.

Are trailing commas in tsconfig.json really valid?

Yes. TypeScript's tsconfig.json is a JSONC file (JSON with Comments), not strict JSON. The TypeScript compiler explicitly parses it with a JSONC parser that accepts trailing commas and // line comments. However, if you read tsconfig.json with JSON.parse in a build script, you must first strip comments and trailing commas using the jsonc-parser package.

How do I prevent trailing commas in generated JSON?

Use a serializer instead of string concatenation. In Python: json.dumps(data, indent=2). In Node.js: JSON.stringify(data, null, 2). In Go: json.MarshalIndent. Serializers model separators between elements rather than appending after each element, so they cannot produce trailing commas. Add python3 -m json.tool as a post-generation validation step in your Makefile or CI script.

Does the JSON5 format allow trailing commas?

Yes. JSON5 (json5.org) explicitly allows trailing commas in objects and arrays, along with single-quoted strings, unquoted keys, and comments. It is a deliberate superset of JSON designed for hand-authoring. To parse JSON5 in Node.js, use the json5 npm package. Never feed JSON5 to JSON.parse — the two formats are incompatible at the parser level.

Related guides

All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-07.