Repository navigation
Built-in validation fixes can silently drop an anchor from blocks that render it on a nested element #83603
Description
Description
Followup from the PR comment here -- #82836 (review)
applyBuiltInValidationFixes() removes an anchor or ariaLabel the block's markup no longer carries. That is right for a hand-edited block, but the helper also runs from the parser and the deprecation path, where nobody edited any markup, so a block that went invalid for an unrelated reason can lose its anchor.
The loss is silent: removing the anchor also removes the id from the save output, so the block usually validates afterwards and renders with no warning.
Introduced in #82836, which added the removal branch to fixGlobalAttribute.
Scope
Core blocks are not affected. Each one with anchor support renders the id on the first top-level element of its save output, so getHTMLRootElement() finds it, and blocks whose save output is inner blocks only are skipped by the existing innerHTML.trim() guard.
Affected are third-party apiVersion 2 blocks that spread useBlockProps.save() onto a nested element — the id is in the markup but invisible to a root-only check - and old deprecations of the same shape.
The exposure is narrower than it looks: serializeBlock() returns serializeRawBlock( block.__unstableBlockSource ) for an invalid block, so one that stays invalid keeps its original source on save. The loss only reaches stored content when removing the anchor is what makes the block validate.
Step-by-step reproduction instructions
No core block has this shape, so the repro registers one from the browser console. Nothing needs building or installing.
- Open a new post in the editor, then open the browser console and paste:
const el = wp.element.createElement;
const { useBlockProps } = wp.blockEditor;
wp.blocks.registerBlockType( 'test/nested-anchor', {
apiVersion: 2,
title: 'Nested anchor test',
category: 'text',
supports: { anchor: true },
edit: () => el( 'div', useBlockProps(), 'Content' ),
save: () => el( 'div', null, el( 'p', useBlockProps.save(), 'Content' ) ),
} );- Open the code editor (
Ctrl/Cmd + Shift + Alt + M) and paste this block. Theidis present but sits on the inner<p>, and the text differs from the save output so the block is invalid for a reason unrelated to the anchor:
<!-- wp:test/nested-anchor {"anchor":"keep-me"} -->
<div><p class="wp-block-test-nested-anchor" id="keep-me">Content changed</p></div>
<!-- /wp:test/nested-anchor -->- Switch back to the visual editor. The block shows the usual invalid-content warning.
- In the console, read the parsed block:
wp.data.select( 'core/block-editor' ).getBlocks()
.map( ( b ) => ( { isValid: b.isValid, anchor: b.attributes.anchor } ) )Expected
anchor: 'keep-me'. The markup still carries id="keep-me", just not on the root element.
Actual
anchor: undefined. The anchor has been removed although nobody edited any markup. Clicking Attempt Block Recovery then writes the block out without the id, making the loss permanent.
Possible fix
Before deleting, check whether the value the block holds still appears as that attribute on any element in the markup, not only on the root:
if (
innerHTML.trim() &&
! isValueInMarkup(
innerHTML,
attributeSchema.attribute,
modifiedBlockAttributes[ supportKey ]
)
) {
delete modifiedBlockAttributes[ supportKey ];
}Scoping the removal to hand-edited markup instead is not viable: the parser cannot tell a code editor edit from stored post content, since both arrive through the same path, so that would break deleting an id in the code editor — one of the flows #82836 fixes.
Related
- Fix: Preserve HTML anchors in Edit as HTML and Classic block conversion (7.0 regression) #82836 — added the removal branch.
- HTML anchors are lost when editing block HTML or converting Classic blocks (7.0 regression) #82683, Classic block conversion silently drops HTML anchors on headings and paragraphs #83118 — the Block support: Preserve anchor value in comment delimiter #70993 regression that Fix: Preserve HTML anchors in Edit as HTML and Classic block conversion (7.0 regression) #82836 addresses.
Screenshots, screen recording, code snippet
No response
Environment info
- WordPress Latest.
- GB trunk.
- Needs changes from Fix: Preserve HTML anchors in Edit as HTML and Classic block conversion (7.0 regression) #82836
Please confirm that you have searched existing issues in the repo.
- Yes
Please confirm that you have tested with all plugins deactivated except Gutenberg.
- Yes
Please confirm which theme type you used for testing.
- Block
- Classic
- Hybrid (e.g. classic with theme.json)
- Not sure
Activity
drzraf commentedon Oct 3, 2026
Not specific to
anchorbut clearest framing of this problem among the bugtracker:The same holds for a third-party attribute stored in the block comment delimiter that no schema declares: it is dropped at parse time, the block is valid before and after, and the loss reaches stored content on the next save.
validity is what removes the protection.
Reported with reproduction under #38922, since that issue already states the "preserve what you can't recognise" principle and names re-generation from working attributes as the mechanism.
Flagging it here only so the two are not fixed in isolation: if #83605 ends up narrowing when
applyBuiltInValidationFixes()may discard data, it may be worth checking whether the resulting rule also wants to cover "the parser found a delimiter key nothing declares", which is the same question asked about a different field./Opus 5
hbhalodia commentedon Oct 22, 2026