karatelabs/karate · error · JsErrorException (typeError)

cannot set property on null 'super' base

Error message

cannot set property on null 'super' base

What it means

In `super.prop = value` assignments, if the superclass lookup finds the home object's [[Prototype]] is null, there is no base object to write to. The engine deliberately raises this TypeError only after the RHS is evaluated (matching PutValue ordering) rather than treating it as a scope update.

Solutions

  1. Restore the class's prototype chain so [[Prototype]] is not null.
  2. Avoid `setPrototypeOf(..., null)` on classes using `super` property writes.
  3. Replace `super.x = v` with an explicit write to the intended parent object when exotic hierarchies are in play.

Example fix

// before
Object.setPrototypeOf(Derived.prototype, null); // super.x = 1 now throws
// after
Object.setPrototypeOf(Derived.prototype, Base.prototype);
Defensive patterns

Strategy: validation

Validate before calling

// ensure the class prototype chain is intact before using super writes
if (Object.getPrototypeOf(Derived.prototype) === null) throw new Error('Derived has null prototype; super writes will fail');

Type guard

function hasSuperBase(C) { return Object.getPrototypeOf(C.prototype) !== null; }

Try / catch

try { instance.step(); } catch (e) { if (String(e.message).includes("null 'super' base")) { restorePrototype(instance.constructor); } else { throw e; } }

Prevention

When it happens

Trigger: A derived class method executes `super.x = v` while the class's home object has a null prototype (e.g. `Object.setPrototypeOf(cls.prototype, null)` or a class created without a proper prototype chain).

Common situations: Manual prototype surgery that nulls the parent; classes built via exotic factories without correct prototypes; framework code that resets prototype chains.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/b1ce4a524cc6d536. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/PropertyAccess.java:336

                // Same slot-or-name routing as set(): a scope-confined let/const
                // is reachable only through its stamped slot — it has no byName
                // entry, so a name-keyed update would miss it and fall through to
                // an outer binding or an implicit global.
                set(node, context, value, trackingNode);
                yield value;
            }
            case REF_DOT_EXPR, REF_BRACKET_EXPR -> {
                AccessSite site = resolveWriteSite(node, context);
                if (context.isStopped()) yield Terms.UNDEFINED;
                Object value = Interpreter.eval(rhsNode, context);
                if (site == null || site == SHORT_CIRCUIT_SITE || context.isStopped()) yield value;
                if (site.target == null && node.getFirst().type == NodeType.SUPER_EXPR) {
                    // A super reference whose home object has a null [[Prototype]]
                    // resolves to a null base; PutValue on it is a TypeError —
                    // AFTER the RHS has been evaluated (hence not in
                    // resolveWriteSite). Without this, setByName's null-target
                    // fallback would treat the write as a scope update.
                    throw JsErrorException.typeError("cannot set property on null 'super' base");
                }
                if (site.privateName != null) PrivateAccess.set(site.target, site.privateName, value, context);
                else siteWrite(site, value, context, trackingNode);
                yield value;
            }
            default -> throw JsErrorException.typeError("cannot set on: " + node);
        };
    }

    /**
     * Compound assignment (`op=`, e.g. +=, -=, *=) in spec §13.15 order:
     * resolve the LHS Reference, GetValue it, and only then evaluate the RHS —
     * a computed-key or getter side effect precedes any RHS side effect, and
     * an abrupt completion at each step skips the rest. Returns the new value.
     */
    static Object compound(Node node, CoreContext context, TokenType operator, Node rhsNode, Node trackingNode) {
                return switch (node.type) {
            case REF_EXPR -> {

View on GitHub (pinned to a22eb90246)