karatelabs/karate · error · JsErrorException (typeError)

unexpected set [null]

Error message

unexpected set [null]:${value} on: ${object}

What it means

An internal TypeError raised by Karate's setByName when the property name argument is null — i.e. an assignment whose target name could not be resolved to a string key. This is an invariant guard: normal `obj.x = v` syntax always supplies a name, so hitting it means the engine tried to write through a malformed/unresolved property reference (e.g. a computed key that evaluated to null in an internal path).

Solutions

  1. Inspect the assignment expression in the feature/script and ensure the property key resolves to a non-null string (e.g. `obj[expr] = v` where `expr` must not be null).
  2. Add a fallback for computed keys: `* def key = expr || 'defaultKey'` before `obj[key] = value`.
  3. If calling the engine API directly, assert the name is non-null before calling setByName / update.
  4. Report upstream if it occurs with plain syntax — it indicates an internal invariant violation.

Example fix

// before (key may be null)
* eval obj[someKey] = 5
// after
* def safeKey = someKey || 'fallback'
* eval obj[safeKey] = 5
Defensive patterns

Strategy: validation

Validate before calling

// before dynamic writes
if (key == null) key = 'defaultKey';

Type guard

function safeKey(k) { return (k != null && typeof k === 'string') ? k : String(k); }

Prevention

When it happens

Trigger: A property-set operation routed into setByName with name == null: internal path where a computed/member key resolves to null, or a bridge/Embedded JS integration calling setByName programmatically with a null key.

Common situations: Rare in hand-written features; typically seen from custom JS engine extensions, external bridge integrations, or dynamic property writes where a key expression produced null instead of a string.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

     * when {@code Array.prototype.{push, unshift}} stores at index 0), routes
     * {@code length} through {@code JsArray.handleLengthAssign} for the spec
     * Uint32 + writable + partial-truncate dance, and otherwise falls through
     * to {@code putMember}. Package-private so {@code JsArrayPrototype.{push,
     * unshift}} can do per-item Set in the spec sequence.
     */
    static void setByName(Object object, String name, Object value, CoreContext context, Node trackingNode) {
        setByName(object, name, value, context, trackingNode, object);
    }

    /** Receiver-aware variant for super references (§10.1.9 OrdinarySet with a
     *  distinct receiver): the accessor lookup walks {@code object}'s chain —
     *  the super base — but a setter runs with {@code receiver} as its
     *  {@code this}, and a data write creates/updates the property on
     *  {@code receiver}, never on the shared prototype. All non-super callers
     *  pass {@code object} itself via the delegating overload above. */
    static void setByName(Object object, String name, Object value, CoreContext context, Node trackingNode, Object receiver) {
        if (name == null) {
            throw JsErrorException.typeError("unexpected set [null]:" + value + " on: " + object);
        }
        if (object == null) {
            context.update(name, value, trackingNode);
        } else if (object instanceof ObjectLike objectLike) {
            // Spec ArraySetLength dispatch needs context for valueOf/toString
            // coercion; route through the JsArray-specific entry point.
            // Throws RangeError on invalid Uint32; silently ignores writable=false
            // and partial-truncate failures (lenient mode — strict-mode TypeError
            // flip lives elsewhere).
            if (objectLike instanceof JsArray ja && "length".equals(name)) {
                Object oldLen = ja.size();
                ja.handleLengthAssign(value, context);
                firePropertySet(context, name, ja.size(), oldLen, object, trackingNode);
                return;
            }
            // If an accessor descriptor lives at `name` anywhere in the
            // prototype chain, invoke its setter via slot.write —
            // bypassing putMember preserves the descriptor and threads

View on GitHub (pinned to a22eb90246)