karatelabs/karate · error · JsErrorException (typeError)
' ' was defined without a getter
Error message
'${pn.name}' was defined without a getter What it means
When reading a private accessor (`accessor get/set` pair) via `PrivateAccess.get`, the engine found the private name declared but only with a setter (or neither), so there is no getter to invoke. Per the ES spec, reading an accessor private field without a getter is a TypeError.
Solutions
- Add a getter to the accessor declaration for the private name.
- Remove the read expression if the member is intentionally write-only.
- Change the member to a plain private field (`#prop = ...`) if read-write value storage is wanted.
- Check that the class version being loaded actually defines the getter.
Example fix
// before
class C { accessor set #x(v) { this._x = v; } }
new C().#x; // TypeError: '#x' was defined without a getter
// after
class C { accessor #x; } // or: get #x() { return this._x; } Defensive patterns
Strategy: type-guard
Validate before calling
// inspect the class declaration for a getter on the private accessor const hasGetter = /get\s+#prop\b|accessor\s+#prop\b/.test(classSource);
Type guard
function privateAccessorReadable(classSrc, name) { return new RegExp('(get\\s+#' + name + '|accessor\\s+#' + name + ')').test(classSrc); } Try / catch
try { v = obj.#prop; } catch (e) { if (e instanceof TypeError && e.message.includes('without a getter')) { /* fall back to underlying field */ } else { throw e; } } Prevention
- Always declare accessor pairs together (get and set).
- Avoid removing a getter while read sites remain — grep for reads first.
- Prefer plain private fields for simple read/write storage.
When it happens
Trigger: Reading `obj.#prop` (or `#prop in`-style read) where the class declared `accessor #prop` with only a `set` accessor, or declared the private name without a get accessor.
Common situations: Write-only private accessors used in expressions; a refactor that removed the getter but left read sites; partial class declarations where the accessor pair was split across class updates.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- ' ' was defined without a setter
- Cannot write to private method
- Cannot private member from an object whose class did not…
- toBytes() argument must be a list of numbers, got
- toBytes() list must contain only numbers, got
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/db17443a2d3583ed.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/PrivateAccess.java:57
PrivateName pn = context.privateEnv == null ? null : context.privateEnv.resolve(name);
if (pn == null) {
throw JsErrorException.syntaxError("private name " + name + " is not declared in an enclosing class");
}
return pn;
}
static boolean has(Object target, PrivateName pn) {
return target instanceof JsObject obj && obj.hasPrivate(pn);
}
static Object get(Object target, PrivateName pn, CoreContext context) {
JsObject obj = branded(target, pn, "read");
return switch (pn.kind) {
case FIELD -> obj.getPrivate(pn);
case METHOD -> pn.method;
case ACCESSOR -> {
if (pn.getter == null) {
throw JsErrorException.typeError("'" + pn.name + "' was defined without a getter");
}
yield Interpreter.invokeGetter(pn.getter, target, context);
}
};
}
static void set(Object target, PrivateName pn, Object value, CoreContext context) {
JsObject obj = branded(target, pn, "write");
switch (pn.kind) {
case FIELD -> obj.putPrivate(pn, value);
case METHOD -> throw JsErrorException.typeError("Cannot write to private method " + pn.name);
case ACCESSOR -> {
if (pn.setter == null) {
throw JsErrorException.typeError("'" + pn.name + "' was defined without a setter");
}
Interpreter.invokeSetter(pn.setter, target, value, context);
}
}View on GitHub (pinned to a22eb90246)