Read the JSON-LD error, not the guess
Invalid JSON-LD is the most expensive structured-data fault there is, because nothing is partially read: one bad character and the whole block is discarded, so a page with perfect Product markup is treated exactly like a page with none. The report usually shows you the parser's own message — and then, underneath, a list of likely causes that may have nothing to do with it.
The tool had the answer and printed a guess
A check that finds invalid JSON-LD has, by definition, just tried to parse it and caught an exception. It is holding the exact reason. Docket printed that exception and then offered a fixed list of three usual suspects: a trailing comma, an unescaped quote, HTML entities the CMS encoded into the block.
On a consumer-rights body's site, every page checked failed with the same message —
Invalid control character — which is none of those three. The real cause was a raw
newline inside a string holding HTML, exactly the shape a CMS produces when it writes a
multi-line field straight into a script block.
So the reader goes hunting for a trailing comma that does not exist, while the message that would have told them the answer sits above it meaning nothing to anybody who does not write parsers. An error message is evidence; a list of likely causes is a guess. Printing the guess while holding the evidence is the defect.
What each message actually means
This is the part worth keeping. These are the messages a JSON parser emits and what each one means for the person fixing it:
Invalid control character— a literal newline, tab or carriage return inside a string value. JSON forbids them unescaped, so write them as\nand\t, or strip them. Almost always a CMS writing multi-line HTML into a field.Expecting property name enclosed in double quotes— a trailing comma before a closing brace or bracket, or a key quoted with apostrophes instead of double quotes.Expecting ',' delimiter— an unescaped double quote inside a string value. Write it as\".Expecting value— an empty field emitted with nothing after the colon, or HTML entities such as"that the CMS encoded into the script block.Extra data— two JSON documents in one script block. Give each its own block, or combine them into a single array.Unterminated string starting at— a string that never closes, usually an unescaped quote or a template cut off mid-render.Invalid \escape— a lone backslash, typically from a Windows path or a regular expression. Double it.
If your tool shows you a message that is not in a list like this, the message is still the better clue than any generic advice beside it. Search for it exactly.
Fixing one error per page is not fixing the page
The second half of this, and the more damaging one.
Docket used to stop at the first broken block on each page. That made its summary sentence unable to be wrong in the only way that matters: "all of them fail the same way" was a statement about what the tool had collected, not about the site. The clause that exists to say "other kinds of error are also present" could never fire, because a second error on an already-recorded page was never looked at.
On a hardware vendor's site every page carried two JSON-LD blocks, and several had both broken, with different errors. The report named one missing comma. Somebody fixes the comma, re-tests, and the page is still discarded by the search engine — which is exactly the outcome the check exists to prevent.
Sampling one instance per page and then summarising across pages produces a claim that cannot be falsified in the way that matters. It is a quiet failure mode, because the summary sounds more confident the less it looked at.
The repair kept the units straight, which is worth copying: the page list stays one entry per page, so the count and the URLs still mean pages, while the error collection gathers every failure so the summary describes the site rather than the sample.
Checking your own markup
- Validate the rendered output, not the template. The commonest cause is a CMS field interpolated into JSON without escaping, so it breaks only for the products whose descriptions happen to contain a quote or a line break — which is why it survives spot-checking.
- Fix every block on the page, then re-validate. One page can carry several, and a page is discarded if any block it depends on is invalid.
- Read the parser message first. Thirty seconds with the list above beats an afternoon with a generic checklist.
- Check the records most likely to contain punctuation — product descriptions with quotation marks, addresses with line breaks, anything pasted from a word processor.
When this matters most, and when it does not
It matters most on templated pages that carry commercial markup — products, events, recipes, jobs — because the fault is invisible on the page and total in effect. There is no partial credit.
It matters least where the block was aspirational: markup added for an entity you were never going to earn a rich result for costs you an entity signal rather than a search feature. Fix it, but not first.
The fixes that make it worse
- Hand-editing the rendered HTML. The next build overwrites it, and you have learned nothing about the template that produced it.
- Stripping the field that broke. Removing the description to make the JSON parse gives you valid markup that describes less than it should.
- Escaping by search-and-replace across the template. Doubling every backslash or quote without knowing which one broke tends to produce a second, different parse error.
How to make this finding disappear without fixing anything
Delete the script block. The finding clears, the page becomes one with no structured data at all, and no tool will complain — because an absent block and a discarded block look identical to a search engine. That equivalence is the whole reason this fault is worth catching: you already have the outcome of deleting it, and you are paying to generate it.
Where this sits in an audit
The registered check is schema.invalid, which covers JSON-LD that does not parse.
For the other structured-data faults — markup contradicting the visible page, required properties
genuinely missing, coverage as a judgement rather than an error — see
how to fix structured data errors →. For
the case where the block parses perfectly and still resolves to nothing, see
an @id is a pointer, not a definition →.
Common questions
What does Invalid control character mean in JSON-LD?
A literal newline, tab or carriage return inside a string value. JSON forbids them unescaped, and it is almost always a CMS writing a multi-line HTML field straight into the script block.
My audit lists likely causes that do not match my error. Which do I trust?
The error message. The tool parsed your block and caught the exact exception; a list of usual suspects beside it is a guess that may predate your case.
If one block on a page is invalid, does the rest still count?
Invalid JSON is not partially read — the whole block is discarded. A page can carry several blocks, so fix every one and re-validate rather than stopping at the first.
Why did fixing the reported error not fix my page?
Because there was more than one. A check that records a single error per page can report that they all fail the same way when it only ever looked at one of them.
How do I find which page broke when the template is fine?
Validate the rendered output rather than the template. These faults come from data interpolated without escaping, so they appear only on the records whose text happens to contain a quote or a line break.