A client of mine had spent days on a custom “Media Portfolio” block we inherited from another agency. The symptom was small and infuriating: a user edited an image caption, saved the post, and nothing happened. Other times the caption saved, then reverted the moment they touched another part of the block. The cause was a desync between two different ways WordPress was tracking media behind the scenes.
My first idea was to hard reset the REST API cache every time the block took focus, which is the kind of fix you reach for when you are in a hurry. It is also the wrong one. It costs performance and leaves the race condition sitting exactly where it was. The actual fix comes out of the WordPress 6.9 editor changes, which clean up how the Block Editor handles data entities.
The media and attachment entity mess
For years the @wordpress/core-data package kept two records for the same thing: a media entity and an attachment entity. Both hit the same REST API endpoint, but each had its own cache. Update one, read the other somewhere else in the UI, and you get stale data. That split is behind a lot of the “ghost bugs” people run into in complex builds.
In WordPress 6.9, per the official dev notes, the media entity is deprecated and everything moves to attachment. The refactor for your data calls looks like this:
// The old, deprecated way
wp.data.select( 'core' ).getMedia( 123 );
// The bbioon-approved, stable way for 6.9+
wp.data.select( 'core' ).getEntityRecord( 'postType', 'attachment', 123 );
// If you're dispatching updates
wp.data.dispatch( 'core' ).saveEntityRecord( 'postType', 'attachment', 123, {
caption: { raw: 'New bbioon Caption' }
} );
The caption is the part that catches people. The attachment entity returns it as an object with raw and rendered properties, while the old media entity sometimes flattened it to a plain string. Any component that expects a string breaks on the object, and if it renders somewhere shared, the whole site goes with it.
Atomic updates with setAttributes
The other change worth your time in the WordPress 6.9 editor changes is that setAttributes now accepts updater functions. Before this, two events firing in quick succession, say two toggle switches clicked within a millisecond, could clobber each other. Both of them calculated a new state from a current state that had already moved on. That is a textbook race condition.
You can now pass a pure function instead, which always receives the latest attribute state:
const bbioonToggleFeature = () => {
setAttributes( ( currentAttributes ) => ( {
isFeatureEnabled: ! currentAttributes.isFeatureEnabled,
} ) );
};
Watch out for SelectControl CSS shifts
This one is for anyone who does a lot of custom editor styling. The SelectControl component moved its class names from an internal div up to the root element. A selector like .components-base-control .my-custom-class can stop matching, because those classes now sit on the same element instead of nesting. It is a small change that will still wreck a carefully styled admin screen.
Why the migration is worth doing
The editor has grown into a data-driven application, and core now maintains it like one. Console warnings are the least of your reasons to move off a deprecated entity like media. The real one is not getting a call at 10 PM because a client’s images are “acting weird.” The old get_posts transitions went the same way, with less JavaScript involved.
This gets complicated fast. If you would rather not spend your week debugging someone else’s block against the latest core standards, drop my team a line. We have probably seen it before.
Are your custom blocks still calling the old media selectors, or have you already moved them over to the attachment entity?