Summary
A function-nested class extending an Error-descendant, with no explicit constructor, drops the arguments passed to new — the implicit derived constructor fails to forward them to super(...). Every error built through effect's makeException factory therefore has message === undefined, and every failure in an effect app prints as "An error has occurred" instead of its real message (e.g. the web.ts startup failure surfaces as a message-less RuntimeException, hiding whatever dieMessage(...) text node would show).
Minimal repro
class Y extends Error {}
const make2 = (): any => {
class Plain extends Y {} // no captures, no statics, no ctor
return Plain
}
const P2 = make2()
console.log(new P2("direct").message) // node: "direct" | perry: undefined
Matrix (probe40, node → perry)
| case |
node |
perry |
implicit ctor, nested class w/ captured field (new A("boom")) |
"boom" |
undefined |
implicit ctor, nested class, no captures (new P2("direct")) |
"direct" |
undefined |
implicit ctor, class expression w/ capture (new C3("expr-msg")) |
"expr-msg" |
undefined |
explicit constructor(msg?){ super(msg) } + capture |
"explicit" |
"explicit" ✓ |
The explicit-forwarding case works, so the defect is specifically the synthesized default derived constructor (spec: constructor(...args) { super(...args) }) not forwarding args — at least for classes whose base chain reaches the native Error.
Impact
Found while unmasking the web.ts startup failure: with the #6465 per-evaluation fix applied, the real error is a RuntimeException whose message was dropped by this bug — the actual failure text is still unknown until this is fixed.
Summary
A function-nested class extending an
Error-descendant, with no explicit constructor, drops the arguments passed tonew— the implicit derived constructor fails to forward them tosuper(...). Every error built through effect'smakeExceptionfactory therefore hasmessage === undefined, and every failure in an effect app prints as"An error has occurred"instead of its real message (e.g. the web.ts startup failure surfaces as a message-lessRuntimeException, hiding whateverdieMessage(...)text node would show).Minimal repro
Matrix (probe40, node → perry)
new A("boom"))"boom"new P2("direct"))"direct"new C3("expr-msg"))"expr-msg"constructor(msg?){ super(msg) }+ capture"explicit""explicit"✓The explicit-forwarding case works, so the defect is specifically the synthesized default derived constructor (spec:
constructor(...args) { super(...args) }) not forwarding args — at least for classes whose base chain reaches the nativeError.Impact
makeExceptionclasses (RuntimeException,InterruptedException,TimeoutException, …) all lose their message:Cause.prettyrenders"An error has occurred"for every failure, destroying diagnosability of any effect app under perry.web.ts(the remaining app of the 4; discovered after fix(hir): per-evaluation statics for class expressions with heritage (#6438) #6449 → fix(runtime): resolve [Symbol.iterator] on a class declaration (#6454) #6460 → module init: nested dep-init calls ignore the topo sort's cycle-breaks — alias exports capture undefined (FindMyWay.make undefined; blocks effect web.ts) #6463 → class declarations in function bodies share one class across evaluations — effect's makeException masks every error as TimeoutException #6465 layers).Found while unmasking the web.ts startup failure: with the #6465 per-evaluation fix applied, the real error is a
RuntimeExceptionwhose message was dropped by this bug — the actual failure text is still unknown until this is fixed.