Skip to main content

2 posts tagged with "Gutenberg"

View all tags

WordPress FSE Block Validation Failed: The Hidden Cause of Missing JSON Quotes

· 4 min read

TL;DR

In WordPress FSE themes, if a JSON attribute in a Pattern/Template HTML comment has a missing closing quote ", the brace count remains balanced, but parse_blocks() silently sets the block's attrs to null. Gutenberg's save function then produces no inline styles, triggering Block validation failed. Validate JSON with json.loads() to catch this.

The Problem

Opening the wishlist template in WordPress Site Editor shows a console error:

Block validation: Block validation failed for `core/group`

Content generated by `save` function:
<div class="wp-block-group has-border-color has-neutral-200-border-color"></div>

Content retrieved from post body:
<div class="wp-block-group has-border-color has-neutral-200-border-color"
style="border-style:solid;border-width:1px;border-radius:var(--wp--custom--border--radius--lg);
padding-top:var(--wp--preset--spacing--40);...">

The save function outputs correct CSS classes but completely loses inline styles, and the content is empty — even though the file clearly contains style attributes and child blocks.

Root Cause

The issue is in the JSON attributes of a core/group block HTML comment:

<!-- Broken -->
<!-- wp:group {"style":{"spacing":{"padding":{"top":"var(--wp--preset--spacing--40)",
"right":"var(--wp--preset--spacing--40)",
"bottom":"var(--wp--preset--spacing--40)",
"left":"var(--wp--preset--spacing--40)"}}, <!-- missing closing quote -->
"border":{"radius":"var(--wp--custom--border--radius--lg)","width":"1px","style":"solid"}},
"borderColor":"neutral-200","layout":{"type":"constrained"}} -->

The "left" value "var(--wp--preset--spacing--40)" is missing its closing quote ". It's written as "var(--wp--preset--spacing--40).

Why brace counting misses this:

Correct: { "left": "value" }  → quotes paired, braces balanced
Broken: { "left": "value } → quotes unpaired, but braces still balanced

When the closing quote is missing, the } characters are treated as string content by the JSON parser (the quote never closed), so the brace count stays balanced.

parse_blocks() doesn't throw an error — it silently sets attrs to null:

// What parse_blocks returns
[
'blockName' => 'core/group',
'attrs' => null, // entire attribute object discarded
'innerHTML' => '<div ...>', // raw HTML still present
]

Gutenberg calls save() with null attrs, produces no inline styles, and the mismatch triggers Block validation failed.

Why this is hard to spot:

  • No white screen — the page still renders (falls back to innerHTML)
  • Braces are balanced, so visual inspection easily misses it
  • Site Editor shows a subtle "block needs recovery" notice
  • Audit scripts typically check brace balance and attribute correspondence, not JSON validity

Solution

1. Locate the Problem

Validate JSON with Python:

python3 -c "
import json
with open('templates/wishlist.html') as f:
content = f.read()
marker = 'wp:group {'
start = content.index(marker) + len(marker) - 1
end = content.index(' -->', start)
json_str = content[start:end]
try:
json.loads(json_str)
print('JSON OK')
except json.JSONDecodeError as e:
print(f'Error at position {e.pos}: {e.msg}')
print(f'Context: ...{json_str[max(0,e.pos-20):e.pos+20]}...')
"

Output pinpoints the exact error:

Error at position 196: Expecting ',' delimiter
Context: ...g--40)}},"border":{"...

2. Fix the JSON

Add the missing closing quote after the "left" value:

<!-- Fixed -->
<!-- wp:group {"style":{"spacing":{"padding":{"top":"var(--wp--preset--spacing--40)",
"right":"var(--wp--preset--spacing--40)",
"bottom":"var(--wp--preset--spacing--40)",
"left":"var(--wp--preset--spacing--40)"}}, <!-- closing quote added -->
"border":{"radius":"var(--wp--custom--border--radius--lg)","width":"1px","style":"solid"}},
"borderColor":"neutral-200","layout":{"type":"constrained"}} -->

3. Verify the Fix

# Verify parse_blocks correctly parses the block
docker exec wp_cli wp eval '
$blocks = parse_blocks(file_get_contents(get_stylesheet_directory() . "/templates/wishlist.html"));
echo $blocks[...]["attrs"]["style"]["border"]["radius"];
' --allow-root

4. Prevention

Add a JSON comment validity check to CI:

import json, re, sys

def check_block_json(filepath):
with open(filepath) as f:
content = f.read()
for m in re.finditer(r'<!-- wp:\w+ (\{.*?\}) -->', content):
try:
json.loads(m.group(1))
except json.JSONDecodeError as e:
print(f"{filepath}: JSON error at comment position {m.start()}: {e}")
sys.exit(1)

check_block_json(sys.argv[1])

Interested in similar solutions? Get in touch

Fix Gutenberg Gradient Class Naming Change Causing Block Validation Failure

· 2 min read

Encountered this issue while developing a WordPress FSE theme for a client. Here's the root cause and solution.

TL;DR

After a Gutenberg upgrade, the gradient CSS class naming convention changed from has-{slug}-gradient to has-{slug}-gradient-background. Hand-written Pattern HTML with old classes doesn't match Gutenberg's validation logic, causing Site Editor errors. The fix is to batch replace class names.

Problem

All legacy Patterns show an error in Site Editor:

Block contains unexpected or invalid content
  • New Patterns work fine
  • Frontend renders correctly
  • Clicking "Attempt Recovery" restores display

Root Cause

Check DevTools Console for Block validation failed logs, compare Expected vs Actual:

Expected (Gutenberg generated):

<div class="wp-block-group has-accent-gradient-background has-background">

Actual (hand-written in Pattern):

<div class="wp-block-group has-accent-gradient has-background">

After Gutenberg upgrade, gradient class naming changed:

Old NamingNew Naming
has-{slug}-gradienthas-{slug}-gradient-background

During Block validation, Gutenberg recalculates expected HTML based on JSON attributes in block comments (e.g., "gradient":"accent-gradient") and compares with actual HTML. Class mismatch triggers validation failure.

Solution

1. Identify Affected Files

# Find files using old class naming
grep -r "has-[a-z0-9-]*-gradient " patterns/ --include="*.php"

2. Batch Replace

# macOS/Linux compatible
sed -i '' 's/has-\([a-z0-9-]*\)-gradient /has-\1-gradient-background /g' patterns/*.php

# Linux (GNU sed)
sed -i 's/has-\([a-z0-9-]*\)-gradient /has-\1-gradient-background /g' patterns/*.php

Notes:

  • Only replace class attributes in HTML tags
  • Block comment declarations like "gradient":"accent-gradient" don't need changes
  • Regex ends with a space to avoid matching has-accent-gradient-background

3. Verify Fix

  1. Clear WordPress cache: wp cache flush
  2. Refresh Site Editor, confirm errors are gone
  3. Spot-check a few Patterns, verify frontend and editor display correctly

Prevention

  1. Prefer block comment attributes: Set styles via JSON attributes (e.g., "gradient":"accent-gradient") to let Gutenberg auto-generate classes, avoid hand-writing
  2. Watch Gutenberg changelog: Check Breaking Changes before upgrading
  3. Test in development first: After upgrade, test all Patterns in dev environment before deploying

Interested in similar solutions? Contact us