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
- Use optional chaining `obj.nullNode?.id` so a missing node degrades to undefined and matches `#notpresent`.
- Guard the parent: `* def id = obj.nullNode ? obj.nullNode.id : null` or match the parent with `#present` first.
- Fix the upstream data so the intermediate field exists.
- 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
- Use ?. for every chained read over optional response fields.
- Match intermediate nodes with #present before drilling in.
- Don't rely on scope-variable fallbacks for missing nested fields.
- Centralize payload extraction in helper features with guards.
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
- cannot read properties of
- toBytes() argument must be a list of numbers, got
- toBytes() list must contain only numbers, got
- xmlPath() first argument must be XML node or string, but was
- object is null
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 theView on GitHub (pinned to a22eb90246)