Repository navigation
cc 2.1.112 on current main: compiles cleanly, then dies at startup with "Cannot set property errorCode of #<VR8> which has only a getter" #11499
Description
On current main (f3828c7ec), claude-code 2.1.112 compiles cleanly but still dies at startup — with a different error from the one in #11301:
Uncaught (in promise) TypeError: Cannot set property errorCode of #<VR8> which has only a getter
at OBY (<anonymous>)
at L (<anonymous>)
at new __AnonShape_6e5125e2965aca2f (<anonymous>)
at L (<anonymous>)
at new __AnonShape_0f21577b01597d8e (<anonymous>)
at L (<anonymous>)
at N8 [0x5d9bd60f333f] (<anonymous>)
--version works (2.1.112 (Claude Code)); --help and doctor both fail identically. This is the current blocker for any cc-based runtime measurement.
First: the reported compile failure does NOT reproduce
A parallel lane reported "ENTRY MODULE FAILED TO COMPILE — REFUSING TO LINK" for this bundle on a90cd9d44, and a separate IR dump found a function __44728 with undefined SSA values. Neither appears here. On f3828c7ec:
COMPILE_RC=0 (2 h 38 m wall)
Wrote executable: /root/rig9831/ccMAIN
Binary size: 313.4MB
The full 676-line log (kept unfiltered) contains no __44728, no verifier failure, and no "REFUSING TO LINK". Its only notices are the expected AOT-unsupported sites (3 Function(...), 1 eval(...), 1 import(...), process._rawDebug) plus one unknown identifier 'MessageEvent' warning. Built with compiler and runtime from the same tree (--no-auto-optimize --enable-wasm-runtime), run under an exclusive heavy-job lock with 28 GB of headroom reserved — the earlier failure was at 24.2 GB peak while other lanes compiled, so resource starvation is the likelier explanation for that report than a codegen defect.
The construct
XX = class XX extends Error {
constructor(q, K) { super(q); this.errorUri = K, this.name = this.constructor.name }
get errorCode() { return this.constructor.errorCode } // INSTANCE accessor, getter-only
};
VR8 = class VR8 extends XX {};
VR8.errorCode = "invalid_request"; // a STATIC assignment on the class object
Sy6 = class Sy6 extends XX {}; Sy6.errorCode = "invalid_client";
// …a dozen more subclasses in the same shapeThe getter is an instance accessor (it lives on XX.prototype). VR8.errorCode = … is a static set on the constructor. Perry reports that the class object has a getter-only errorCode, i.e. the instance accessor is visible on the static side, so the static assignment hits it and throws. In node the same code runs and hasOwnProperty is true on both the class and the prototype.
The new __AnonShape_* frames match the anonymous class … extends XX {} expressions, and L is esbuild's __esm lazy-init wrapper, so this runs during module initialisation.
Characterisation
- Not GC-sensitive — byte-identical first line under
PERRY_GEN_GC=0andPERRY_GC_FORCE_EVACUATE=1. - Deterministic, and independent of
HOME. - The cc 2.1.112 compiled from main dies at startup: every subcommand but --version throws "value is not a function" #11301 failure is gone:
value is not a functionno longer appears at all (grep count 0), so this is a distinct, later failure rather than the same one re-surfacing. - Not reproduced minimally. Nine reductions match node exactly, including: a base-class instance getter with a static set on an anonymous subclass (the shape above, verbatim); a class field shadowing an inherited getter-only accessor; the same in an anonymous class expression; and a constructor assignment over a declared field. As in cc 2.1.112 compiled from main dies at startup: every subcommand but --version throws "value is not a function" #11301, the reductions appear to remove the trigger rather than the failing path — cc reaches it through
__esmlazy init in a 47,000-closure program.
Note for #11301 / #11469
The commits named there as fixing #11174, c025870294 and 256925ac46, are not ancestors of f3828c7ec — git merge-base --is-ancestor reports both absent from current main. Worth confirming where they actually live before treating that fix as landed.
Activity
proggeramlug commentedon Sep 27, 2026
Correction: the #11174 fixes ARE in this arm — my ancestry note above was wrong
The closing note in the issue body said
c025870294and256925ac46are not ancestors off3828c7ec. That is false. Re-checked from a freshly fetched clone:They landed as #11226 / #11263 on 09-24/25. My check ran against a repo whose ref set was incomplete — that host's shared repo had been re-seeded from another clone, so
merge-baseanswered against refs it did not have. Ancestry from a re-seeded or partially fetched repo is not evidence; that is my error, not a problem with the fix.This matters in the right direction: it explains the good news in this issue. The #11301 failure (
value is not a functionfrom an unregisteredMath) is gone in this binary precisely because the #11174 fix is present — the no-auto HTTP rebuild no longer bundles a runtime built withoutperry-runtime's default features. So that diagnosis is corroborated here, and theerrorCodeaccessor failure below is a genuinely separate, later bug rather than the same one wearing a new face.proggeramlug commentedon Sep 27, 2026
Root cause: a STATIC write on a class constructor is resolved against the INSTANCE accessor chain
Confirmed in the debugger on the symbolized build (
ccMAIN_sym,f3828c7ec), breaking onclass_chain_setter_applywith the key filtered toerrorCode:The receiver is the class constructor object (
VR8 [Error]), not an instance — and it carries a non-zeroclass_id. Native path:Why that is wrong
In the bundle:
VR8.errorCode = …is anOrdinarySeton the constructor. The constructor's prototype chain is the static side —VR8 → XX → Error → Function.prototype— and there is noerrorCodeaccessor anywhere on it. The getter lives onXX.prototype, which is not on the constructor's chain.set_field_by_name_object_tail(atobject/field_set_by_name/tail.rs:384in this tree) guards only onclass_id != 0before consultingclass_chain_setter_apply, i.e. the instance accessor chain. Since the constructor object carries the class'sclass_id, a static write is resolved against instance accessors, finds the getter-onlyerrorCode, and takes theSome(false)arm:The comment above that arm — "Class accessors are properties of the class prototype chain" — is true for instances. The missing condition is that the receiver must be an ordinary instance, not the class object itself.
Fix shape
Apply the instance accessor chain only when the receiver is not a class constructor. A regression test should pin the bundle's shape directly, since it is ordinary ES semantics:
Worth pinning the same for a static data write shadowed by an instance getter of the same name on any base in the chain, and for
Reflect.set(VR8, "errorCode", v)— the failing path here runs throughordinary_set_with_receiver, so the receiver-carrying entry point deserves its own case.Notes on two suspects raised elsewhere
class_idhere is 7515, not 0.OBY, butOBYin the bundle is a render loop (function OBY(q,K,_,z,Y,A){let{width,height,index}=q; …}) and is uniquely defined — so it is a nearest-symbol artefact, not the writer. The native backtrace above is the one to work from.PERRY_GEN_GC=0andPERRY_GC_FORCE_EVACUATE=1.test(gap): pin a class-object static write against the instance acces…
test(gap): pin a class-object static write against the instance acces…
fix(runtime): a property write on a class OBJECT resolves the static …
test(gap): pin a class-object static write against the instance acces…