Skip to content

Structured dataPart of: Structured data

Schema markup validation: fix structured data errors and warnings

Schema markup validation checks three things in order: the JSON-LD parses, the types and required properties are right, and the page qualifies for a rich result. Fix errors in that order, then warnings.

getReport teamUpdated 27 Sept 202613 min read

Schema markup validation answers three different questions, and most confusion comes from asking one tool all three: does the JSON-LD parse, are the types and required properties right, and does the page qualify for a rich result? Fix them in that order. A syntax error hides everything after it, a missing required property makes eligibility moot, and a warning about a recommended property can wait until both are clean. For the basics of what markup to add, start with our guide to schema markup and the types that matter.

Quick answer

LayerQuestionTool
1. SyntaxDoes the JSON parse?getReport's parse finding, jq, the script below
2. ShapeAre the types and properties real, and are the required ones present?schema.org's Schema Markup Validator for vocabulary, getReport's required-properties finding for common types
3. EligibilityDoes Google offer a rich result for this type, and does the page qualify?Google's Rich Results Test, the final authority
  • Errors first, warnings second. An error (Google calls it a critical issue) makes the item invalid; a warning is a missing recommended property.
  • Test one page per template, not only the home page. Each template prints different entities.
  • One source per entity. Two Organization or Product blocks are a duplicate, not a backup.
  • Make the values match the page. Valid markup with a price or rating visitors cannot see breaks Google's guidelines.
  • Run the free schema markup validator first; it answers layers 1 and 2 on the live page in one pass.

Errors and warnings: what validators actually report

Each tool uses its own words for the same few problems:

ToolBlocking problemNon-blocking problem
Google Rich Results TestCritical issue: the item is invalid and cannot show a rich resultNon-critical issue: a recommended property is missing or wrong
Search Console enhancement reportsInvalid item, with an error such as Missing field "offers"Valid item with warnings, such as Missing field "priceValidUntil"
Search Console Unparsable structured data reportParsing error: Missing ',' or '}', Bad escape sequence in stringnone
Schema Markup Validator (validator.schema.org)Error: invalid JSON or an unknown type or propertyWarning: a value of an unexpected type
getReport"JSON-LD cannot be parsed", "Required properties are missing"Recommended fields are left to Google's test

The Rich Results Test only reports on types that earn a Google rich result. Google removed FAQ support from it in June 2026, after FAQ rich results stopped appearing on 7 May 2026, and HowTo was dropped in 2023. For those types, check syntax and shape with getReport or validator.schema.org.

How getReport checks it

The validator reads every <script type="application/ld+json"> block in the HTML your server sends, parses each one, and flattens the result into top-level entities: a root object, the members of a top-level array and the members of @graph. It then checks each entity against a table of required properties.

The JSON-LD parse finding opened on a page with one broken block: "1 of 1 JSON-LD blocks cannot be parsed" in red, the explanation that one stray comma costs the whole block, and two fix steps pointing to the technical detail with the parser message and block number
The technical detail names the block and the parser's message with its position, which is where to start.

What the required-properties table covers

Dotted paths look inside nested objects, an array must have the property on every element, and empty strings and empty arrays count as missing.

Type (subtypes treated the same)Required in getReport's table
Productname, image, offers, offers.price or offers.lowPrice, offers.priceCurrency
Article, NewsArticle, BlogPostingheadline, author, datePublished, image
BreadcrumbListitemListElement; each item needs position and name, and item on all but the last
FAQPagemainEntity, mainEntity.name, mainEntity.acceptedAnswer.text
LocalBusiness and its subtypesname, address, telephone
Eventname, startDate, location
Recipename, image
VideoObjectname, thumbnailUrl, uploadDate
ReviewitemReviewed, author, reviewRating
AggregateRatingratingValue, ratingCount or reviewCount
Organization, WebSitename, url

The table follows Google's documentation where it documents a rich result, and is stricter in places (Google lists no required properties for Article). Types outside it, such as JobPosting, are listed but not checked, and eligibility means the required properties are present, not that Google will show the result.

Common errors and how to fix each

Message (wording varies by tool)Usual causeFix
Missing ',' or '}', "Expected double-quoted property name"A trailing comma, a missing comma, or two blocks pasted into oneRemove the trailing comma; one object or @graph per script tag
Bad escape sequence in string, unexpected tokenA double quote or line break inside a product name or descriptionBuild the JSON with an encoder (JSON.stringify, wp_json_encode), not string templates
Block not found at all</script> inside a string ended the tag early, or JavaScript adds the block after loadWrite <\/script>; print the block in the server HTML
Missing field "offers", Either "offers", "review", or "aggregateRating" should be specifiedA product with no price in the CMS, or a theme that prints Product on category pagesFill in the price; print Product only on product pages
Missing field "image"A post without a featured imageSet a featured image, or a default in the SEO plugin
Invalid value in field "availability""InStock" or "in stock" instead of the full URLUse https://schema.org/InStock
Invalid price"€129" or "129,00""price": "129.00" plus "priceCurrency": "EUR"
Invalid date or datetime26/09/2026 or a date without a timezoneISO 8601: 2026-09-26T09:00:00+02:00
Invalid object type for field "brand"A plain string where an object is expected{ "@type": "Brand", "name": "Alpina" }
Unknown type or property"Prodcut", "datepublished": names are case-sensitiveCorrect the spelling; unknown names are silently ignored
Missing field "priceValidUntil" (warning)A recommended property left outOptional; add it when the price has a real end date

Smart quotes (“ ”) from a word processor and HTML entities such as &quot; written in by a CMS filter cause the same parse failures, because entities are not decoded inside a script element.

Step by step

1. Fix syntax first

The parse finding lists each failed block as block N: with the parser's message and position, for example "Expected double-quoted property name in JSON at position 140 (line 6 column 1)". Count the blocks in the page source from the top; the position counts from the start of that block. To check one block locally, save it to a file and run either command; both print the line and column of the first error:

Shell
jq . block.json
node -e 'JSON.parse(require("fs").readFileSync(0, "utf8"))' < block.json

2. Check the vocabulary

A block that parses can still use a misspelled type or property. The Schema Markup Validator, run by schema.org, checks names against the vocabulary and shows the parsed tree, which also exposes nesting mistakes. It says nothing about what Google requires.

3. Fix required properties at the source

Run the validator on one page per template: home, article, product, category, location. For each missing property, fix the source rather than the output: an empty price field, a post without a featured image, an author profile with no name. Required first; recommended properties (sku, brand, dateModified) make a result richer, not possible.

4. Remove duplicates

Two blocks describing the same Organization or Product are a problem the per-entity checks cannot see. The duplicate-entities finding flags more than one root Organization or WebSite, and any other type repeated with the same name and url or @id. Keep one source per entity and connect the rest with @id references. On WordPress this is usually two plugins; our guide to schema markup in WordPress shows which generator to keep and how to switch off the others.

5. Make the values match the page

The price in the markup must be the price on the page, the rating must be visible, and FAQ questions must be on the page. No validator checks this, and it is the most common reason valid markup earns nothing. Google's structured data guidelines allow a manual action for markup that misrepresents the page.

6. Catch regressions in CI

This script checks layers 1 and 2 for a URL or a built HTML file. It needs Node 18 or later and no packages. Save it as scripts/check-jsonld.mjs:

JavaScript
// node check-jsonld.mjs <url-or-file> [more…]; exits 1 on a parse error or missing property.
import { readFile } from 'node:fs/promises';

const REQUIRED = {
  Product: ['name', 'image', 'offers'],
  Article: ['headline', 'author', 'datePublished', 'image'],
  BlogPosting: ['headline', 'author', 'datePublished', 'image'],
  BreadcrumbList: ['itemListElement'],
  Organization: ['name', 'url'],
};
const load = (s) => (/^https?:\/\//.test(s) ? fetch(s).then((r) => r.text()) : readFile(s, 'utf8'));
const blocksOf = (html) =>
  [...html.matchAll(/<script\b[^>]*type=["']?application\/ld\+json["']?[^>]*>([\s\S]*?)<\/script>/gi)].map((m) => m[1]);
const roots = (n, out = []) => {
  if (Array.isArray(n)) n.forEach((x) => roots(x, out));
  else if (n && typeof n === 'object') {
    if (n['@type']) out.push(n);
    if (Array.isArray(n['@graph'])) roots(n['@graph'], out);
  }
  return out;
};
const present = (v) => v != null && v !== '' && !(Array.isArray(v) && !v.length);

let failed = false;
for (const src of process.argv.slice(2)) {
  const seen = new Map();
  blocksOf(await load(src)).forEach((raw, i) => {
    let data;
    try { data = JSON.parse(raw); } catch (e) { failed = true; return console.log(`${src} block ${i + 1}: ${e.message}`); }
    for (const e of roots(data)) {
      const type = [].concat(e['@type'])[0];
      const key = `${type} ${e['@id'] ?? e.url ?? e.name ?? ''}`;
      seen.set(key, (seen.get(key) ?? 0) + 1);
      const missing = (REQUIRED[type] ?? []).filter((p) => !present(e[p]));
      if (missing.length) { failed = true; console.log(`${src} block ${i + 1}: ${type} missing ${missing.join(', ')}`); }
    }
  });
  for (const [key, n] of seen) if (n > 1) console.log(`${src}: ${key} appears ${n} times`);
}
process.exit(failed ? 1 : 0);

Run it against the built pages of a static site, or a staging URL for a server-rendered one. A GitHub Actions step:

YAML
- name: Check structured data
  run: node scripts/check-jsonld.mjs dist/index.html dist/blog/first-post/index.html dist/products/winter-boot/index.html

The build fails when a template prints broken JSON or drops a required property, the regression nobody notices by eye.

7. Confirm with Google, then ask it to recheck

Once layers 1 and 2 pass, run the Rich Results Test on one URL per template. It renders the page like Googlebot and separates critical from non-critical issues. For site-wide errors in Search Console, open the enhancement report, fix the template, and click Validate fix so Google recrawls the affected URLs; validation can take days to weeks.

Schema added with JavaScript

getReport reads the HTML your server sends. A block added by a tag manager or client-side framework after load does not appear in its findings, although Google may read it after rendering. To see what the browser ended up with, paste this into the DevTools console:

JavaScript
[...document.querySelectorAll('script[type="application/ld+json"]')].forEach((s, i) => {
  try { JSON.parse(s.textContent); console.log(`block ${i + 1}: parses`); }
  catch (e) { console.log(`block ${i + 1}: ${e.message}`); }
});

Printing the block in the server HTML is the more reliable choice: every consumer sees it, not only the ones that run JavaScript.

getReport only detects microdata and does not parse RDFa. Move the data into one JSON-LD block when you next touch the template.

Verify

  • The schema validator shows "All N JSON-LD blocks parse", required properties passed for each type, and no duplicate entities.
  • node scripts/check-jsonld.mjs https://example.com/products/winter-boot/ exits with 0.
  • The Rich Results Test detects the same types with no critical issues, and Search Console's valid items grow over the following weeks.

Common mistakes

  • Trusting a plugin's green tick. Plugins check their settings, not the output after other plugins and filters have touched it.
  • Treating warnings as errors. Hours spent on recommended properties while a required one is missing.
  • Assuming valid means visible. Eligibility is per type, and Google decides per search whether to show a rich result.

Questions people ask

Why is my schema valid but no rich results show?

Because valid markup only makes a page eligible; Google decides per search whether to show a rich result. Common reasons: the type no longer earns one (HowTo ended in 2023, FAQ on 7 May 2026), a property that result needs is missing, the page is not indexed yet, or the marked-up content is not visible on the page. Allow a few weeks after indexing before drawing conclusions.

What is the difference between a schema error and a warning?

An error means the item is invalid: the JSON does not parse or a required property is missing, so it cannot earn a rich result. A warning, which Google calls a non-critical issue, means a recommended property is missing or has a questionable value; the item still qualifies, just with less detail. Fix every error first, then add recommended properties where you have real data for them.

How do I fix "Missing field" errors in Search Console?

Open the enhancement report, click the issue and look at the example URLs; the message names the missing property, such as offers or image. Add it to the JSON-LD in your template or plugin settings, using real data shown on the page. Warnings about optional fields can wait. Then click Validate fix so Google recrawls the affected pages.

Should I use JSON-LD or microdata?

JSON-LD. Google recommends it because it sits in one script block apart from the visible HTML, so theme changes cannot break it by accident, and it is easier to validate. Microdata and RDFa still work, but mixing formats for the same entity often creates duplicates. The schema validator checks JSON-LD in full and detects microdata.

What replaced Google's Structured Data Testing Tool?

Two tools. Google retired the Structured Data Testing Tool in 2021: rich-result checks moved to the Rich Results Test, and general vocabulary checks moved to schema.org as the Schema Markup Validator, which says nothing about Google eligibility. getReport's validator parses every block and checks required properties for the common rich-result types; Google's test stays the final word.

Check your site before and after Check