mirror of
https://github.com/penpot/penpot.git
synced 2026-03-28 06:10:28 +01:00
🐛 Fix fetch abort errors escaping the unhandled exception handler (#8801)
When AbortController.abort(reason) is called with a custom reason (a
ClojureScript ExceptionInfo), modern browsers (Chrome 98+, Firefox 97+)
reject the fetch promise with that reason object directly instead of with
the canonical DOMException{name:'AbortError'}. The ExceptionInfo has
.name === 'Error', so both the p/catch guard and is-ignorable-exception?
failed to recognise it as an abort, letting it surface to users as an
error toast.
Fix by calling .abort() without a reason so the browser always produces
a native DOMException whose .name is 'AbortError', which is correctly
handled by all existing guards.
Also add a defense-in-depth check in is-ignorable-exception? that
filters errors whose message matches the 'fetch to \'' prefix, guarding
against any future re-introduction of a custom abort reason.
Co-authored-by: Penpot Dev <dev@penpot.app>
This commit is contained in:
@@ -355,10 +355,11 @@
|
||||
(= message "Unexpected end of input")
|
||||
(str/starts-with? message "invalid props on component")
|
||||
(str/starts-with? message "Unexpected token ")
|
||||
;; Abort errors are expected when an in-flight HTTP request is
|
||||
;; cancelled (e.g. via RxJS unsubscription / take-until). They
|
||||
;; are handled gracefully inside app.util.http/fetch and must
|
||||
;; NOT be surfaced as application errors.
|
||||
;; Native AbortError DOMException: raised when an in-flight
|
||||
;; HTTP fetch is cancelled via AbortController (e.g. by an
|
||||
;; RxJS unsubscription / take-until chain). These are
|
||||
;; handled gracefully inside app.util.http/fetch and must NOT
|
||||
;; be surfaced as application errors.
|
||||
(= (.-name ^js cause) "AbortError"))))
|
||||
|
||||
(on-unhandled-error [event]
|
||||
|
||||
@@ -127,11 +127,17 @@
|
||||
(fn []
|
||||
(vreset! unsubscribed? true)
|
||||
(when @abortable?
|
||||
;; Provide an explicit reason so that the resulting AbortError carries
|
||||
;; a meaningful message instead of the browser default
|
||||
;; "signal is aborted without reason".
|
||||
(.abort ^js controller (ex-info (str "fetch to '" uri "' is aborted")
|
||||
{:uri uri}))))))))
|
||||
;; Do NOT pass a custom reason to .abort(): browsers that support
|
||||
;; AbortController reason (Chrome 98+, Firefox 97+) would reject
|
||||
;; the fetch promise with the supplied value directly. When that
|
||||
;; value is a ClojureScript ExceptionInfo its `.name` property is
|
||||
;; "Error", not "AbortError", which defeats every existing guard
|
||||
;; that checks `(= (.-name cause) "AbortError")`. Calling .abort
|
||||
;; without a reason always produces a native DOMException whose
|
||||
;; `.name` is "AbortError", which is correctly recognised and
|
||||
;; suppressed by both the p/catch handler and the global
|
||||
;; unhandled-exception filter.
|
||||
(.abort ^js controller)))))))
|
||||
|
||||
(defn response->map
|
||||
[response]
|
||||
|
||||
Reference in New Issue
Block a user