Skip to content

Fix: Built-in validation fixes can silently drop an anchor from blocks that render it on a nested element - #83605

Closed
hbhalodia wants to merge 25 commits into
WordPress:trunkfrom
hbhalodia:fix/issue-83603
Closed

hbhalodia wants to merge 25 commits into
WordPress:trunkfrom
hbhalodia:fix/issue-83603

Conversation

@hbhalodia

Copy link
Copy Markdown
Contributor

What?

Closes #83603

Keeps an anchor or ariaLabel that the block's markup still carries on a nested element, instead of removing it.

Why?

applyBuiltInValidationFixes removes one of these attributes when the markup no longer carries it, added in #82836 so that deleting an id by hand does not leave the block stuck in recovery.

The check reads only the root element, because getHTMLRootElement parses with a [data-anchor] > * selector. A block that spreads useBlockProps.save() onto a nested element still carries the id, just deeper, so it looked as though the attribute had been deleted.

That branch also runs from the parser and the deprecation path, where nobody edited any markup. There, a block that went invalid for an unrelated reason silently lost its anchor.

No core block has this shape: each one with anchor support renders the id on the first top-level element of its save output, and blocks whose save output is inner blocks only are skipped by the existing innerHTML.trim() guard. The exposure is third-party apiVersion 2 blocks and old deprecations.

How?

The removal branch now confirms the value is absent from the whole markup before deleting it:

} else if (
	innerHTML.trim() &&
	! hasHTMLAttributeValue(
		innerHTML,
		attributeSchema.attribute,
		modifiedBlockAttributes[ supportKey ]
	)
) {

hasHTMLAttributeValue matches on the value rather than on the attribute being present anywhere, so a block whose inner blocks carry their own anchors is not mistaken for one that still has its own.

Testing Instructions

No core block can reach this, so the repro registers one from the browser console. Nothing needs building.

  1. Open a new post, then paste into the browser console:
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' ) ),
} );
  1. Open the code editor (Ctrl/Cmd + Shift + Alt + M) and paste. The id is 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 -->
  1. Switch back to the visual editor, then read the parsed block in the console:
wp.data.select( 'core/block-editor' ).getBlocks()
	.map( ( b ) => ( { isValid: b.isValid, anchor: b.attributes.anchor } ) )

Before: anchor: undefined, removed although the markup still has it.
After: anchor: 'keep-me'.

Regression check

Deleting an id for real must still drop the anchor. Paste this into the code editor and switch to the visual editor — the heading should render without a warning and Advanced → HTML anchor should be empty:

<!-- wp:heading {"anchor":"keep-me"} -->
<h2 class="wp-block-heading">Hello</h2>
<!-- /wp:heading -->

Testing Instructions for Keyboard

N/A

Screenshots or screencast

N/A

Use of AI Tools

  • Claude Code, Opus 5.5.
  • Used for diagnosis, implementation, PR description.
  • The fix was tested and reviewed by me.

@github-actions github-actions Bot added [Package] Blocks /packages/blocks [Package] Block editor /packages/block-editor labels Sep 28, 2026
@github-actions

Copy link
Copy Markdown

🤖 PR meta 🤖

🏷️ Labels

This pull request needs exactly one label indicating its type, and has 0.

  • Required: any label starting with [Type].
  • Found: none.

Read more about Type labels in Gutenberg. If you cannot add labels yourself, a reviewer can do it for you.

@hbhalodia

Copy link
Copy Markdown
Contributor Author

@hbhalodia hbhalodia closed this Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Block editor /packages/block-editor [Package] Blocks /packages/blocks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Built-in validation fixes can silently drop an anchor from blocks that render it on a nested element

2 participants