You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Error subclass name is an own enumerable property: JSON.stringify(err) is {"name":"Error"} vs node {}, and getOwnPropertyNames omits stack #9440
classMyErrextendsError{}conste=newMyErr("boom");JSON.stringify(e)// node {} perry {"name":"Error"}Object.getOwnPropertyNames(e)// node ["message","stack"] perry ["message","name"]
Two divergences from one cause. Errors carry a dedicated ErrorHeader with a name field (crates/perry-runtime/src/error.rs) that the own-key walk surfaces as own and enumerable. Node keeps name on Error.prototype, so it is not an own property at all and never appears in JSON.stringify or getOwnPropertyNames.
The stack half is separate and already addressed in #9432, which installs an own non-enumerablestack accessor. name is untouched by that PR — one of its comments even records the current state as "enumerability, different reflection — a deliberate simplification". This issue is to retire that simplification.
Why it matters beyond reflection
Serializing an error is routine in logging, IPC and session transcripts, so the wrong shape travels: a consumer that does JSON.stringify(err) gets a name key node never emits, and anything reconstructing an error from getOwnPropertyNames misses stack.
It was checked as a candidate cause of #9421 (cc transcripts truncated) and ruled out — it changes serialized content, not record count — but it is a real defect on its own.
Fix shape
name should live on the prototype like node's, with the ErrorHeader field kept as the storage behind it rather than exposed as an own key. Anything that special-cases name in the own-key walk (js_object_keys, getOwnPropertyNames, the JSON own-key path) needs to stop reporting it.
Watch the subclass case specifically: class MyErr extends Error {} produces a GC_TYPE_OBJECT rather than an ErrorHeader (see #9410), so the two paths must agree after the change. extends_builtin_error(class_id) is the existing disambiguator.
Verification bar
A gap fixture byte-compared to node --experimental-strip-types, demonstrated failing on a compiler built from unfixed origin/main, covering: Error itself and a subclass; JSON.stringify; Object.getOwnPropertyNames; Object.keys; a for…in loop; spread into an object literal; an explicitly assigned err.name = "Custom" (which must become an own enumerable property, as in node); and util.inspect output.
Found by the differential stress-test of claude-code under perry, while falsifying #9421's async-flush premise.
Activity
fix(runtime): inherit Error subclass names (#9440)
fix(runtime): inherit Error subclass names (#9440)
train: trim #9538 to the live #9450 half — the #9440 half landed via #…
train: trim #9538 to the live #9450 half — the #9440 half landed via #…