How to lint json files for syntax errors
- Step 1Drop the JSON file to lint — Drag the file onto the dropzone or click to choose it. Linting runs in your browser via
JSON.parse. Free tier accepts up to 2 MB, one file at a time. - Step 2Click Validate — Parsing is strict RFC 8259 regardless of the 'Strict mode' checkbox. Click Validate to run the linter pass.
- Step 3Read the first error — On a red result you get the raw parser message and a character offset.
JSON.parsestops at the first error, so fix that one and re-run to surface the next — linting is iterative by nature. - Step 4Use the pretty output as your clean view — On green, the 'Parsed output' panel is your re-indented, normalised JSON. Skim it for structural surprises a syntax pass won't flag — a value at the wrong depth, or a duplicate key whose first value silently vanished. (Preview truncates at 5,000 chars; Copy gives the full output.)
- Step 5Auto-fix instead of hand-fixing — If the file has several mechanical mistakes (single quotes, trailing commas,
undefined, unquoted keys), run it through json-format-fixer, which applies regex repairs and re-parses, returning corrected JSON. - Step 6Add a CLI linter for repo-wide passes — For a project-wide lint, install jsonlint (
npm i -g jsonlint) or usejq empty, then rungit ls-files '*.json' | xargs -I{} sh -c 'jq empty {} || echo BAD {}'. Same RFC 8259 rules, applied to every file.
What this linter catches vs what it cannot
Grounded in JSON.parse behaviour. The 'cannot' rows are real limits, not oversights — they need a different parser than V8 exposes.
| Lint concern | Caught here? | Why / what to use instead |
|---|---|---|
| Trailing commas | Yes | Rejected by strict parse |
| Single quotes / unquoted keys | Yes | Rejected by strict parse |
| Comments (JSONC) | Yes — rejected | Strict JSON has no comments |
| Missing comma between elements | Yes | Expected ',' or ']' … |
NaN/Infinity/+1/hex/leading zeros | Yes — rejected | Valid JS, invalid JSON |
| Bad string escapes / raw control chars | Yes | Bad escaped character / Bad control character |
| Duplicate keys | No | V8 silently keeps the last value — review by eye |
| Excessive nesting depth | No | No depth check; deep JSON still passes |
| HTML entities / encoding issues in values | No | Values are treated as opaque strings |
Linter vs the right specialised tool
This linter reports. Other tools repair, reshape, or compare. Pick by intent.
| Intent | Use | Notes |
|---|---|---|
| See the first syntax error + position | This linter | Read-only, authoritative |
| Auto-repair common mistakes | json-format-fixer | Fixes quotes/commas/undefined, re-parses |
| Re-indent / pretty-print | json-prettifier | Formatting-focused |
| Shrink for transport | json-minifier | Strips whitespace |
| Explore deeply nested JSON | json-tree-viewer | Collapsible tree |
| Compare two files | json-diff | Structural diff |
JSON tool tier limits
Per-file size limits. Linting is single-file.
| Tier | Max file size | Batch files |
|---|---|---|
| Free | 2 MB | 1 |
| Pro | 100 MB | 10 |
| Developer | 5 GB | unlimited |
Cookbook
Real lint passes — what gets caught, and the important cases that don't. Values anonymised.
Missing comma between two array elements
ExampleTwo objects in an array with no comma between them. The linter pinpoints the position where the parser expected a separator.
Input:
[
{ "id": 1 }
{ "id": 2 }
]
Linter output:
Invalid JSON
Expected ',' or ']' after array element in JSON at position 18 (line 3 column 3)
Fix: add a comma after { "id": 1 }.Non-finite number from a JS serializer
ExampleA value of NaN slipped in when an upstream JS service serialised an unguarded division. Valid JavaScript, invalid JSON — the linter rejects it.
Input:
{ "ratio": NaN }
Linter output:
Invalid JSON
Unexpected token 'N', "{ "ratio": NaN }" is not valid JSON
Fix the producer to emit null or a number; the linter only reports.Duplicate key — passes the linter (a real limitation)
ExampleThis is the headline limitation. JSON.parse accepts duplicate keys and keeps the last value, so the linter reports 'Valid JSON' and the first value silently disappears from the parsed output. No JSON.parse-based linter catches this.
Input:
{ "role": "admin", "role": "user" }
Linter output:
Valid JSON
Parsed output:
{ "role": "user" } <- first 'role' is gone
Takeaway: compare keys by eye; this is a known blind spot of strict
JSON parsing, not a bug in the tool.Deeply nested but valid JSON — no depth warning
ExampleA 12-level-deep structure is perfectly valid JSON and lints green. The tool does not warn about depth; if maintainability is a concern, that's a design decision to make manually.
Input (abbreviated):
{"a":{"b":{"c":{"d":{"e":{"f":{"g":{ ... }}}}}}}}
Linter output:
Valid JSON
(No depth warning — strict JSON has no nesting limit. Use
json-tree-viewer to navigate deep structures.)Bad escape inside a string value
ExampleA backslash followed by an invalid escape character (a pasted Windows path) fails. The linter reports the position of the offending escape.
Input:
{ "dir": "C:\temp" }
Linter output:
Invalid JSON
Bad escaped character in JSON at position 11
Fix: "C:\\temp" (double the backslash) or use forward slashes.Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Duplicate keys
By design — not flaggedThe most important limitation to understand: {"a":1,"a":2} lints as valid and V8 keeps the last value (2), silently dropping the first. This tool — like every JSON.parse-based linter, including jq and node — does not detect duplicate keys. If duplicate keys matter to you, inspect the file by eye; the parsed output already shows only the survivor.
Deeply nested structures
Supported — no warningThere is no nesting-depth check. A 10- or 20-level-deep object lints green. Deep nesting can be a maintainability smell, but it isn't a JSON error and the tool won't flag it. Use json-tree-viewer to navigate deep structures, or json-flattener to flatten them.
HTML entities or mojibake in string values
Supported — not inspectedValues are opaque strings to a JSON parser. {"name":"José"} or {"name":"José"} are valid JSON — the linter won't warn that the value is HTML-encoded or mis-decoded. Encoding cleanup is a content concern, not a syntax one; handle it before or after parsing.
Trailing comma
Rejected{"a":1,} / [1,2,] fail with Expected double-quoted property name / Expected value. The linter reports the comma's position. To auto-strip them, use json-format-fixer.
Comments (// or /* */)
RejectedStrict JSON has no comments, so any comment fails (Unexpected token /). This is correct linting for strict JSON. If the file is intentionally JSONC (tsconfig), it shouldn't be linted with a strict parser — use a JSONC-aware tool.
`NaN`, `Infinity`, `+1`, hex, leading zeros
RejectedAll are valid JavaScript literals but invalid JSON, so the linter rejects them (Unexpected token/Unexpected number). These usually come from confusing JS object literals with JSON. Convert the value to a finite, JSON-legal number (or null).
Top-level scalar (`42`, `"x"`, `true`, `null`)
SupportedRFC 8259 allows a bare scalar as the whole document, so the linter passes 42 or "x". Some stricter linters historically required an object/array at the root; this one follows the current standard and accepts scalars.
Leading UTF-8 BOM
SupportedA BOM (U+FEFF) at the start is trimmed before parsing (it's whitespace in ECMAScript), so the file lints clean. Note other linters may flag a BOM as a warning; if downstream tools are strict about it, save as UTF-8 without BOM.
Empty or whitespace-only input
RejectedEmpty input fails with Unexpected end of JSON input — there's nothing to lint. The minimal valid documents are {}, [], null, or a bare scalar.
File over the 2 MB free limit
BlockedFree tier caps files at 2 MB. Larger data files lint on Pro (100 MB) or Developer (5 GB). For a quick check of a big file, a CLI jq empty runs locally without a size limit.
Frequently asked questions
Does this linter catch duplicate keys?
No. It runs JSON.parse, which accepts duplicate keys and keeps the last value — so {"a":1,"a":2} lints as valid and the first a silently disappears from the parsed output. This is a fundamental limit of JSON.parse-based linting; jq and node behave identically. If duplicate keys matter, review the raw file by hand.
Does it warn about deeply nested JSON?
No. There's no nesting-depth check — deep structures lint green because deep nesting isn't a JSON error. If you want to navigate or flatten deep JSON, use json-tree-viewer or json-flattener; if you want a depth rule enforced, that needs a dedicated linter with configurable rules, which this tool isn't.
Why does it stop at the first error instead of listing all of them?
Because it uses JSON.parse, which throws on the first syntax error and stops. That's standard for strict JSON parsing. Fix the reported error and re-run to find the next — JSON files don't accumulate many independent errors the way a programming language source file does, so iterating is fast.
How is this different from json-format-fixer?
This linter is read-only: it reports the first error and its position. json-format-fixer is write: it attempts to repair common mistakes (single→double quotes, trailing-comma removal, unquoted-key quoting, undefined→null, missing-comma insertion) and returns corrected JSON. Use the linter to find problems, the fixer to fix them.
Is my JSON uploaded for linting?
No. Linting runs entirely in your browser via JSON.parse. The content never reaches a JAD Apps server — DevTools shows no network request when you click Validate.
Does the 'Strict mode' checkbox switch to a lenient lint mode?
No. Parsing is always strict RFC 8259. The checkbox is shown for clarity but does not enable comment/trailing-comma tolerance. There is no lenient mode in this tool — use json-format-fixer if you need leniency-then-repair.
Will it flag HTML-encoded or mis-encoded text in values?
No. To a JSON parser, string values are opaque — "José" and "José" are both valid JSON. Encoding/entity cleanup is a content-processing concern outside JSON syntax. Handle it separately before or after linting.
Does it accept a top-level number or string?
Yes. RFC 8259 allows any JSON value at the top level, including bare scalars like 42, "hello", true, and null. The linter follows the standard and reports them as valid, unlike some older linters that demanded an object or array root.
Can I lint every JSON file in my repository at once?
Not in the UI — it's one file at a time (free batch is 1). For a repo-wide pass, install jsonlint or use jq empty in a loop: git ls-files '*.json' | xargs -I{} sh -c 'jq empty {} || echo BAD {}'. Same RFC 8259 rules, every file.
Is its verdict the same as jsonlint or jq?
For pure syntax, yes — they all enforce RFC 8259. Some CLI linters add optional checks (duplicate-key warnings, sort checks) that this tool doesn't have. For plain validity, a green here equals a clean jq empty and a clean jsonlint.
Why is the parsed output cut off in the preview?
The on-screen pretty print is capped at 5,000 characters for performance. The valid/invalid verdict covers the whole file, and the Copy button returns the complete re-serialised JSON. The truncation only affects the visible preview.
It lints clean but my app still misbehaves on the data — why?
Clean lint means valid syntax, not correct content. A duplicate key dropped a value, a number is the wrong type for your code, or a field is missing — none of which are syntax errors. Use json-diff to compare against a known-good file, or generate a schema with the JSON Schema Generator to enforce shape.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.