Opened 4 weeks ago
Last modified 3 weeks ago
#65852 accepted defect (bug)
Media grid tile label (aria-label) is stale: shows "uploading…" / "(no title)" for titled attachments whose models load after the view is created
| Reported by: | archon810 | Owned by: | joedolson |
|---|---|---|---|
| Priority: | normal | Milestone: | 7.2 |
| Component: | Media | Version: | 7.0.3 |
| Severity: | normal | Keywords: | has-patch |
| Cc: | Focuses: | ui, accessibility, javascript |
Description (last modified by )
It's been a while since I reported a core WP bug, but I have to because it's a pretty annoying one. Of course, Cursor and Fable 5 assisted me in root-causing, but I assure you I'm a real human (Artem Russakovskii) reporting a real bug.
Since WordPress 7.0, the Media Library grid shows each attachment tile's aria-label as a visible label overlay (.wp-core-ui .attachment:not(:has(.filename))::after { content: attr(aria-label); }, added in [62104] for #64883).
However, wp.media.view.Attachment computes that attribute only once, when the view's element is created - Backbone only evaluates attributes() during _ensureElement():
attributes: function() {
...
return {
'tabIndex': 0,
'role': 'checkbox',
'aria-label': ariaLabel,
'aria-checked': false,
'data-id': this.model.get( 'id' )
};
},
When a tile view is created for an attachment model that has not finished loading yet, the label is computed from an empty model and falls back to "uploading…" (or "(no title)" once [62892] / #65438 ships in 7.1). When the model's data arrives, the view re-renders - but render() only replaces the element's inner HTML, so the stale aria-label (and therefore the visible label and the accessible name) is never corrected. The tile stays labeled "uploading…" forever, even though the attachment has a title.
This is not an edge case. Core itself creates id-only models with a fetch in flight:
wp.media.controller.FeaturedImage.updateSelection()doesattachment = Attachment.get( id ); attachment.fetch();and puts that model into the selection, which is synced into the library. The tile for the current featured image is therefore created before the fetch resolves whenever the AJAX response takes longer than the modal render - i.e. on any real site with normal latency. (Even when thequery-attachmentsresponse later includes the same attachment, its data merges into the existing model instance, which only re-renders the tile's inner HTML - the element attribute stays stale.)- Gutenberg's
MediaUpload(@wordpress/media-utils) uses the samewp.media.attachment( id )+fetch()pattern inonOpenfor current values (featured image, galleries), so the block editor is affected the same way. - Any plugin following the documented
wp.media.attachment( id ).fetch()pattern to preload selections hits this too.
Symptom from a user's perspective: open a post that already has a featured image, click the featured image to replace it - the currently-featured tile in the grid is labeled "uploading…" even though nothing is uploading and the image has a title (the Attachment Details sidebar shows the correct title). Switching to another screen and back (e.g. "Upload files" → "Media Library", or Edit Gallery → Add to Gallery → back) recreates the views and the labels fix themselves, confirming it's a stale attribute rather than missing data.
Note this is distinct from #65438 (milestoned for 7.1): that fix ([62892]) only changes which fallback string attributes() picks for a titleless model. It does not address the staleness. Verified: applying the exact [62892] change to the 7.0.2/7.0.3 reproduction environment below (patched built media-views.js / media-views.min.js, cache-busted) changes the stuck label from "uploading…" to "(no title)" - a permanently wrong label on an attachment that has a title (see attached screenshot. The stale value never updates to the real title in either case.
Screen reader impact: the stale accessible name predates 7.0 (the attribute was always computed once), but 7.0 made it visible to everyone.
Steps to reproduce (stock WordPress)
- Use a site with normal (non-localhost) latency, so the
get-attachmentAJAX response arrives after the media modal has rendered. - Create a post and set as featured image an image that has a title.
- Reload the post edit screen and click the featured image thumbnail to open the "Featured image" modal (classic editor metabox, or Replace in the block editor).
- Look at the selected tile in the Media Library grid.
- Expected: the tile label shows the attachment title.
- Actual: the tile label shows "uploading…" ("(no title)" after [62892]) and never updates. Switching to "Upload files" and back to "Media Library" fixes it.
One-click reproduction in WordPress Playground
Open the one-click reproduction in WordPress Playground (self-contained link; the entire setup below is encoded in the URL).
The blueprint creates a titled image ("My titled image", dated 2020), 5 newer "Filler image N" attachments (for contrast: their labels render correctly next to the broken one), and a post "Featured image label repro" with the 2020 image as its featured image. Because everything in Playground is local and near-instant, the race that occurs naturally on real sites is made deterministic by an mu-plugin that adds a 2-second delay to the get-attachment admin-ajax response (the request issued by wp.media.model.Attachment.fetch()); a second hook makes the media modal default to the Media Library tab so no extra clicks are needed.
To reproduce: open the Playground link, go to Posts → "Featured image label repro" → click the featured image thumbnail in the Featured image metabox. The selected tile shows "uploading…" and keeps it indefinitely, while every other tile is labeled correctly and the Attachment Details sidebar shows the real title. Switch "Upload files" → "Media Library" to watch it self-heal.
Verified on WordPress 7.0.2/7.0.3 / PHP 8.3 in Playground (labels sampled at 0.7 s, 2.7 s, and 5.7 s after opening the modal - the stale label persists while wp.media.attachment( id ).get( 'title' ) returns the correct title). Re-verified with the #65438 fix ([62892]) applied on top: same staleness, label stuck at "(no title)" instead.
Suggested fix
Re-apply the accessible name during render() so it catches up once the model has data, e.g. in src/js/media/views/attachment.js factor the label computation out of attributes():
getAriaLabel: function() {
var ariaLabel = this.model.get( 'title' );
if ( ! ariaLabel ) {
if ( this.model.get( 'uploading' ) ) {
ariaLabel = wp.i18n.__( 'uploading…' );
} else {
ariaLabel = wp.i18n.__( '(no title)' );
}
}
return ariaLabel;
},
and in render() after the template is applied:
// `attributes()` only runs when the view element is created, which can be
// before the model has been fetched. Keep the accessible name in sync.
if ( this.$el.attr( 'aria-label' ) !== undefined ) {
this.$el.attr( 'aria-label', this.getAriaLabel() );
}
The undefined guard skips subclasses that deliberately reset the inherited attributes (wp.media.view.Attachment.Details, see #47458), and leaves aria-checked alone so selection state set by updateSelect() is not clobbered. Views re-render on every model change (rerenderOnModelChange defaults to true), so no new listeners are needed; genuine uploads keep showing "uploading…" until the upload finishes and the server response populates the model.
Attachments (2)
Change History (12)
@
4 weeks ago
An actual screenshot from our customized WordPress instance where the gallery shows all of the items as "uploading..."
#2
in reply to: ↑ 1
@
4 weeks ago
Replying to joedolson:
This is largely a duplicate of #65438, but includes an additional suggestion about the
renderpath that's worth looking into.
Indeed, I reference #65438 several times in the report, but I had Fable test the patch slated for 7.1 from there (also as mentioned in the report), and the bug is still present, albeit instead of "uploading...", it shows "(no title)".
#3
@
4 weeks ago
- Description modified (diff)
- Keywords has-test-info has-screenshots added; has-patch removed
- Version → 7.0.3
#5
@
4 weeks ago
Here is the same Playground blueprint but on current trunk showing (no title) instead of uploading....
This ticket was mentioned in Slack in #core-test by softglaze. View the logs.
4 weeks ago
#7
@
4 weeks ago
- Keywords needs-patch added; needs-testing has-test-info has-screenshots removed
Reproduced using the Playground blueprint from the description.
Environment
WordPress: 7.0.3
PHP: 8.3
Browser: Chrome 151.0.7922.71 on macOS
Opened the Featured image label repro post and clicked the thumbnail in the Featured image metabox. The selected tile shows uploading… and keeps it, while the five filler tiles are labeled correctly. The Attachment Details sidebar shows the real title at the same time, so the data has arrived and only the attribute is stale.
Switching to Upload files and back to Media Library clears it, which matches the description.
Happy to open a PR for the render-path change if nobody has started one. @joedolson, would you rather it land here or fold into #65438?
This ticket was mentioned in PR #13053 on WordPress/wordpress-develop by @sureshsornapudi09.
4 weeks ago
#8
- Keywords has-patch added; needs-patch removed
Media Library grid tiles can keep a stale aria-label (uploading… / (no title)) when the view is created for an id-only attachment model that is still fetching. The visible CSS label overlay (added in 7.0) makes that staleness obvious to everyone, not only screen reader users.
## Problem
wp.media.view.Attachment computes aria-label only in attributes(), and Backbone evaluates that once during _ensureElement(). Core (and Gutenberg) often create tiles via Attachment.get( id ) + fetch(), so the label is computed from empty model data and never updates when the model later receives a title — even though render() re-runs on model change.
This is distinct from #65438 / [62892], which only changes which fallback string is used for a titleless model.
## Fix
- Factor the label computation into
getAriaLabel(). - Re-apply
aria-labelat the end ofrender()once the model has data. - Skip subclasses that reset
attributes(e.g.Attachment.Details, see #47458) so we do not add inappropriate attributes there. - Leave
aria-checkedalone so selection state fromupdateSelect()is not clobbered.
## Testing instructions
- Use the Playground blueprint from the Trac ticket, or locally:
- Create a post with a titled featured image.
- Reload the post edit screen and open the Featured image modal.
- Confirm the selected tile label updates to the attachment title (not stuck on
uploading…/(no title)). - Confirm untitled tiles still show
(no title), and in-progress uploads still showuploading…. - Confirm Attachment Details (sidebar) is unchanged (no checkbox/
aria-labelattributes added).
## Use of AI Tools
AI assistance: Yes
Tool(s): Cursor
Model(s): Cursor Grok 4.5
Used for: Implementation assistance following the approach suggested on the Trac ticket. Changes were reviewed against the codebase and existing Attachment.Details attribute reset pattern.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)

This is largely a duplicate of #65438, but includes an additional suggestion about the
renderpath that's worth looking into.