karatelabs/karate · error · JsErrorException

called on null or undefined

Error message

 called on null or undefined

What it means

Karate's JS engine implements the ECMAScript spec preamble for Object.keys/values/entries: they require a coercible object. Calling any of them with null, undefined, or no argument throws a TypeError instead of returning an empty result, matching test262 behavior.

Solutions

  1. Check the value is non-null before calling Object.keys/values/entries: `if (val) Object.keys(val)`.
  2. Use a default: `Object.keys(val || {})`.
  3. Fix upstream logic so the variable is defined before this call (e.g. define it in karate-config.js or guard with karate.get).

Example fix

// before
var names = Object.keys(response.body.user);
// after
var names = Object.keys(response.body.user || {});
Defensive patterns

Strategy: type-guard

Validate before calling

function isCoercibleObject(v) { return v != null; }
if (!isCoercibleObject(val)) throw new Error('expected object for Object.keys');

Type guard

function isObjectLike(v) { return v !== null && typeof v === 'object'; }

Try / catch

try { var keys = Object.keys(val); } catch (e) { if (String(e).indexOf('called on null or undefined') !== -1) { var keys = []; } else { throw e; } }

Prevention

When it happens

Trigger: Object.keys(null), Object.values(undefined), Object.entries() with no args, or any of these called on a variable that resolved to null/undefined at runtime (e.g. a missing JSON response field).

Common situations: Iterating over an API response field that is absent; a Karate variable not yet defined; a function that returns null on failure being passed straight into Object.keys.

Related errors


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

Appendix: source

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

    private Object entries(Context context, Object[] args) {
        requireObjectCoercible(args, "Object.entries");
        CoreContext cc = context instanceof CoreContext c ? c : null;
        List<Object> result = new ArrayList<>();
        for (KeyValue kv : Terms.toIterable(args[0], cc)) {
            List<Object> entry = new ArrayList<>();
            entry.add(kv.key());
            entry.add(kv.value());
            result.add(new JsArray(entry));
        }
        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 —

View on GitHub (pinned to a22eb90246)