karatelabs/karate · error · JsErrorException

Cannot convert undefined or null to object

Error message

Cannot convert undefined or null to object

What it means

Object.assign requires a coercible target object. Per §20.1.2.1, when the first argument is null or undefined the engine throws a TypeError; the target is mutated and returned, so a null target cannot proceed.

Solutions

  1. Ensure the target object exists before assigning: initialize it to {} if missing.
  2. Use Object.assign({}, src) to build a fresh object instead of mutating a possibly-null target.
  3. Guard: `if (target != null) Object.assign(target, src)`.
  4. In Karate JS, back the variable with a default at definition time: `var target = base || {}`.

Example fix

// before
var merged = Object.assign(config.extra, defaults);
// after
var merged = Object.assign(config.extra || {}, defaults);
Defensive patterns

Strategy: type-guard

Validate before calling

if (target == null) { target = {}; }
Object.assign(target, src);

Type guard

function isTarget(v) { return v !== null && v !== undefined; }

Try / catch

try { Object.assign(target, src); } catch (e) { if (String(e).indexOf('Cannot convert undefined or null') !== -1) { target = Object.assign({}, src); } else { throw e; } }

Prevention

When it happens

Trigger: Object.assign(null, src), Object.assign(undefined, {...}), or Object.assign(maybeNull, opts) where maybeNull came from an absent config/JSON field.

Common situations: Merging default options onto a config object that was never initialized; spreading onto a missing response object.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsObjectConstructor.java:227

        }
        return new JsArray(result);
    }

    /** Spec ToObject preamble: Object.keys/values/entries throw TypeError on
     *  null/undefined ahead of any iteration (test262
     *  Object/keys/15.2.3.14-1-4 and -1-5). */
    private static void requireObjectCoercible(Object[] args, String op) {
        if (args.length < 1 || args[0] == null || args[0] == Terms.UNDEFINED) {
            throw JsErrorException.typeError(op + " called on null or undefined");
        }
    }

    private Object assign(Context context, Object[] args) {
        if (args.length == 0) {
            return new LinkedHashMap<>();
        }
        if (args[0] == null || args[0] == Terms.UNDEFINED) {
            throw JsErrorException.typeError("Cannot convert undefined or null to object");
        }
        CoreContext cc = context instanceof CoreContext c ? c : null;
        // §20.1.2.1: the target itself is mutated and returned — identity is
        // observable (`Object.assign(t, src) === t`), and step 4c is
        // Set(to, key, value, true), so target setters fire and a rejected
        // write throws. Spread/rest keep CreateDataProperty semantics via
        // Terms.copyDataProperties; the two operations are not the same seam.
        // ToObject approximation for a primitive target: no mutable wrapper
        // exists, so copy into a fresh map that stands in for the wrapper —
        // identity/setter semantics only apply to real object targets
        Object target = args[0] instanceof ObjectLike || args[0] instanceof Map<?, ?>
                ? args[0] : new LinkedHashMap<String, Object>();
        for (int i = 1; i < args.length; i++) {
            if (cc != null && cc.isError()) {
                break; // a source getter threw — nothing copies past it
            }
            // §20.1.2.1 step 4: both key partitions copy — the symbol store has
            // no string key, so it is walked separately

View on GitHub (pinned to a22eb90246)