karatelabs/karate · error · JsErrorException (typeError)

cannot read properties of

Error message

cannot read properties of ${object} (reading '${name}')

What it means

A TypeError from Karate's name-based property read: the base object was null or undefined when evaluating `base.name`. Karate deliberately does not fall back to a same-named scope variable (a past bug let `a.b.c` resolve `c` from scope when `a.b` was null), so this now fails loudly. Optional access (`?.`) or `optional` mode returns undefined instead.

Solutions

  1. Use optional chaining `obj.nullNode?.id` so a missing node degrades to undefined and matches `#notpresent`.
  2. Guard the parent: `* def id = obj.nullNode ? obj.nullNode.id : null` or match the parent with `#present` first.
  3. Fix the upstream data so the intermediate field exists.
  4. Restructure the match to use Karate fuzzy markers (`##string`, `#notpresent`) instead of direct chained access.

Example fix

// before
* match obj.nullNode.id == '#notpresent'   // throws when nullNode is missing
// after
* match obj.nullNode?.id == '#notpresent'
Defensive patterns

Strategy: type-guard

Validate before calling

* def id = obj?.nullNode?.id   // undefined instead of throw
* match id == '#notpresent'

Type guard

function hasPath(obj, path) { return path.split('.').every(function (k) { return obj != null ? (obj = obj[k], true) : false; }); }

Prevention

When it happens

Trigger: `a.b` where `a` is null/undefined, `match obj.nullNode.id == '...'` when `nullNode` is absent, chained path access in `match`/`assert`/`eval` on a missing response field, JS `karate.get` of a dotted path whose prefix doesn't exist.

Common situations: Reading nested fields from API responses where an intermediate node is missing, Scenario-Outline data where a column is present in some rows' payloads but not others, calling a helper that expects a populated object but receives null.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

                                     CoreContext context, boolean functionCall) {
        return getByName(object, name, optional, context, functionCall, object);
    }

    /** Receiver-aware variant for super references (§13.3.7.3): the lookup
     *  walks {@code object}'s chain, but any getter runs with {@code receiver}
     *  as its {@code this}. All non-super callers pass {@code object} itself
     *  via the delegating overload above. */
    private static Object getByName(Object object, String name, boolean optional,
                                     CoreContext context, boolean functionCall, Object receiver) {
        if (object == null || object == Terms.UNDEFINED) {
            // Reading a property of null/undefined is a TypeError (or undefined under ?.).
            // Do NOT fall back to a same-named scope variable: `a.b.c` where `a.b` is null
            // must not silently resolve to a variable `c` that happens to be in scope. That
            // false resolution made `match obj.nullNode.id == '...'` pass by reading an
            // unrelated `id` binding (e.g. a Scenario-Outline Examples column) instead of
            // degrading to #notpresent.
            if (optional) return Terms.UNDEFINED;
            throw JsErrorException.typeError("cannot read properties of " + object + " (reading '" + name + "')");
        }

        if (object instanceof JsObject jsObj) {
            // Single own-slot probe replaces the historical containsKey +
            // getMember pair (containsKey was exactly getOwnSlot != null, and
            // the own-hit branch of 3-arg getMember is exactly slot.read).
            PropertySlot own = jsObj.getOwnSlot(name);
            if (own != null) {
                return own.read(receiver, context);
            }
            Object result = jsObj.getMember(name, receiver, context);
            if (isFound(result)) return result;
            // JsValue wrappers may carry an original Java value (e.g. JsDate
            // from a ZonedDateTime); route the missed lookup through it via
            // the unified bridge fallback below so native methods like
            // ZonedDateTime.format remain callable. A plain JsObject without
            // an original returns UNDEFINED here — bridge access on a JS-only
            // object would expose wrapper internals and shadow the

View on GitHub (pinned to a22eb90246)