svelte icon indicating copy to clipboard operation
svelte copied to clipboard

fix: avoid writing to not writable error object in handle_error

Open trueadm opened this issue 1 year ago • 1 comments

In web workers, error objects aren't writable. So in that case, there's no point in trying to decorate the error.

trueadm avatar May 17 '24 19:05 trueadm

🦋 Changeset detected

Latest commit: d4366ff4851ef1c67c4add0ca3454e0d64f3eb55

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
svelte Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

changeset-bot[bot] avatar May 17 '24 19:05 changeset-bot[bot]

I just ran into this locally while looking into something else and was able to solve it with Object.defineProperty — any reason not to use that instead?

Rich-Harris avatar May 19 '24 14:05 Rich-Harris

Does that work for workers too?

trueadm avatar May 19 '24 16:05 trueadm

The behaviour I see is quite strange. With a new Error('original message'):

  • in Firefox and Safari overwriting the message works just fine
  • in Chrome I can set the value, and it updates (console.log(e.message) reflects the new value), but the error that's printed to the console contains the original message. Presumably something to do with how the error is cloned (though if I manually structuredClone the error, it works)

The place where I originally saw this was with a DOMException (in the main thread), where the message is a getter on the prototype rather than a property of the error object:

try {
  document.createComment('').appendChild(document.createElement('div'));
} catch (e) {
  console.log(Object.getOwnPropertyDescriptor(e, 'message')); // undefined

  e.message = 'overwritten'; // has no effect
  console.log(e.message); // original message

  Object.defineProperty(e, 'message', { value: 'overwritten' }); 
  console.log(e.message); // 'overwritten'

  throw e; // throws 'overwritten' in Chrome and Safari, original message in Firefox
}

So there's absolutely no consistency between browsers, they're all just making it up as they go along. I'm not sure how to create a DOMException in a worker where there's no DOM; things like SyntaxError which I imagined would be similar are actually more like conventional errors.

In other words I can't answer your question without knowing what error you were dealing with in the first place, because normal errors seem to work just fine (except that the overwritten message is ignored in some situations).

Rich-Harris avatar May 20 '24 13:05 Rich-Harris

I switched to using Object.assign

trueadm avatar May 21 '24 13:05 trueadm

did you test this with the error you encountered with a non-writable message?

Rich-Harris avatar May 21 '24 13:05 Rich-Harris

@Rich-Harris I'll try and find out from the user who reported it.

trueadm avatar May 21 '24 13:05 trueadm

It doesn't seem to be fixed. I created a laughably minimal repro. I guess the error is still thrown by the worker, even though one might expect it to be thrown by the main thread...

Here is a something similar to what I had in my original code of the error in case its useful to have more substance.

EDIT: I notice for some reason the stack trace might not be showing in the REPL, even though the error does. Here is my local dev stack trace:

Uncaught (in promise) TypeError: Cannot set property message of  which has only a getter
    at handle_error (runtime.js:288:8)
    at execute_effect (runtime.js:503:3)
    at flush_queued_effects (runtime.js:568:4)
    at flush_queued_root_effects (runtime.js:548:5)
    at flush_sync (runtime.js:745:4)
    at flush_sync (runtime.js:752:4)
    at mount (render.js:106:9)
    at new Svelte4Component (legacy-client.js:75:49)
    at new <anonymous> (legacy-client.js:47:4)
    at initialize (client.js:434:9)

runtime.js 287-290

const indent = /Firefox/.test(navigator.userAgent) ? '  ' : '\t';
error.message += `\n${component_stack.map((name) => `\n${indent}in ${name}`).join('')}\n`;
      ^^^^^^^
const stack = error.stack;

petermakeswebsites avatar May 26 '24 14:05 petermakeswebsites

Seems to be fixed just fine? Here's the second example running on this branch

Rich-Harris avatar Jun 02 '24 14:06 Rich-Harris