Handle 404 errors gracefully when viewing deleted entities in UI#1829
Handle 404 errors gracefully when viewing deleted entities in UI#1829queelius wants to merge 1 commit intocloudamqp:mainfrom
Conversation
…ent UI When an entity (exchange, queue, connection, channel, vhost, user, or stream) is deleted while its management UI detail page is open, the UI now shows a clear "not found" message with a link back to the list page, instead of the unhelpful "Something went wrong: Error fetching data: [object Object]" error. Changes: - http.js: Add `message` property to error objects so they serialize to readable strings instead of [object Object] - datasource.js: Emit a distinct `not_found` event on 404 responses, and ensure generic errors always pass a string message - table.js: Handle the `not_found` event with a user-friendly message, and fix error display to fall back to string conversion - dom.js: Add `showEntityNotFound()` helper that replaces page content with a "not found" card and a link back to the entity list - Entity detail pages (exchange, queue, connection, channel, vhost, user, stream): Catch 404 errors from the periodic fetch, stop the refresh interval, and show the not-found message Fixes cloudamqp#889 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the management UI’s behavior when a currently-viewed entity is deleted, by handling 404 responses explicitly and presenting user-friendly “not found” messaging instead of generic/opaque errors.
Changes:
- Add a shared
DOM.showEntityNotFound()helper that replaces the page content with an entity-specific “not found” view and a back link. - Update multiple entity detail pages (exchange/queue/stream/connection/channel/user/vhost) to catch 404s, stop refresh intervals where applicable, and show the not-found view.
- Improve error propagation/display by (a) ensuring HTTP error objects have a
message, and (b) adding a datasourcenot_foundevent that tables can render as a friendly message.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| static/js/http.js | Ensures non-OK HTTP error objects include a usable message (avoids [object Object] concatenation). |
| static/js/datasource.js | Emits a distinct not_found event on 404 and normalizes emitted error text. |
| static/js/table.js | Renders datasource 404s as a user-friendly message; improves error-string fallback. |
| static/js/dom.js | Adds showEntityNotFound() helper to replace main content with a not-found view. |
| static/js/exchange.js | Stops periodic exchange refresh and shows not-found view on 404. |
| static/js/queue.js | Stops periodic queue refresh and shows not-found view on 404. |
| static/js/stream.js | Stops periodic stream refresh and shows not-found view on 404. |
| static/js/connection.js | Stops periodic connection refresh and shows not-found view on 404. |
| static/js/channel.js | Stops periodic channel refresh and shows not-found view on 404. |
| static/js/user.js | Shows not-found view on 404 when loading user details. |
| static/js/vhost.js | Shows not-found view on 404 when loading vhost details. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| toggleDisplayError(id, 'Error fetching data: ' + (error.detail || error)) | ||
| }) | ||
| dataSource.on('not_found', () => { | ||
| toggleDisplayError(id, 'This resource no longer exists. It may have been deleted.') | ||
| }) |
There was a problem hiding this comment.
toggleDisplayError() assumes the ${id}-error element exists. After DOM.showEntityNotFound() clears <main>, datasource reload callbacks can still fire and these handlers will throw when the error element is gone. Please guard against missing elements (either inside toggleDisplayError() or before calling it) to prevent runtime exceptions after switching to the not-found view.
| reload (args) { | ||
| clearTimeout(this._reloadTimer) | ||
| return this._reload(args).then(resp => { | ||
| this._enqueueReload() | ||
| this.items = resp | ||
| return resp | ||
| }).catch(err => { | ||
| this._enqueueReload() | ||
| if (err.status === 401) { return } | ||
| if (err.message) { | ||
| this.emit('error', err.message) | ||
| } else { | ||
| this.emit('error', err) | ||
| if (err.status === 404) { | ||
| this.emit('not_found') | ||
| return | ||
| } | ||
| const message = err.message || err.reason || String(err) | ||
| this.emit('error', message) | ||
| }) |
There was a problem hiding this comment.
DataSource.reload() currently calls _enqueueReload() before checking for err.status === 404, so a 404 will still schedule another reload and keep polling a permanently deleted resource. Consider skipping _enqueueReload() for 404 (and clearing any pending timer) so not_found is emitted once and auto-reload stops for that datasource.
| dataSource.on('error', error => { | ||
| console.log(error) | ||
| toggleDisplayError(id, 'Error fetching data: ' + error.detail) | ||
| toggleDisplayError(id, 'Error fetching data: ' + (error.detail || error)) |
There was a problem hiding this comment.
In the table error handler, (error.detail || error) can still stringify to [object CustomEvent]/[object Object] if detail is falsy or not a string. Safer is to use a nullish check and explicitly coerce the chosen value to a string before concatenation.
| toggleDisplayError(id, 'Error fetching data: ' + (error.detail || error)) | |
| const errorDetail = error?.detail ?? error | |
| toggleDisplayError(id, 'Error fetching data: ' + String(errorDetail)) |
Summary
Fixes #889
When an entity (exchange, queue, connection, etc.) is deleted while its management UI detail page is open, the UI previously showed the unhelpful error "Something went wrong: Error fetching data: [object Object]". This PR makes the UI handle 404 responses gracefully:
not_foundevent on 404, which the table renders as a user-friendly message instead of the generic errormessageproperty (preventing[object Object]in string concatenation)Root cause
The error object created in
http.json non-OK responses had nomessageproperty. When the datasource caught this error and tried to emit it, the object was passed through totoggleDisplayError()which concatenated it into a string, producing[object Object]. Additionally, none of the entity detail pages (exchange.js, queue.js, etc.) had.catch()handlers for their periodic API fetch calls.Files changed
static/js/http.js- Addmessageproperty to error objectsstatic/js/datasource.js- Emitnot_foundevent on 404; ensure error messages are always stringsstatic/js/table.js- Handlenot_foundevent; fix error display fallbackstatic/js/dom.js- AddshowEntityNotFound()helper functionstatic/js/exchange.js- Handle 404 on entity fetchstatic/js/queue.js- Handle 404 on entity fetchstatic/js/connection.js- Handle 404 on entity fetchstatic/js/channel.js- Handle 404 on entity fetchstatic/js/vhost.js- Handle 404 on entity fetchstatic/js/user.js- Handle 404 on entity fetchstatic/js/stream.js- Handle 404 on entity fetchTest plan
[object Object]npx standard static/jsto verify linting passes🤖 Generated with Claude Code