{"record":{"id":"db17443a2d3583ed","repo":"karatelabs/karate","slug":"pn-name-was-defined-without-a-getter","errorCode":null,"errorMessage":"'${pn.name}' was defined without a getter","messagePattern":"'(.+?)' was defined without a getter","errorType":"exception","errorClass":"JsErrorException (typeError)","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/PrivateAccess.java","lineNumber":57,"sourceCode":"        PrivateName pn = context.privateEnv == null ? null : context.privateEnv.resolve(name);\n        if (pn == null) {\n            throw JsErrorException.syntaxError(\"private name \" + name + \" is not declared in an enclosing class\");\n        }\n        return pn;\n    }\n\n    static boolean has(Object target, PrivateName pn) {\n        return target instanceof JsObject obj && obj.hasPrivate(pn);\n    }\n\n    static Object get(Object target, PrivateName pn, CoreContext context) {\n        JsObject obj = branded(target, pn, \"read\");\n        return switch (pn.kind) {\n            case FIELD -> obj.getPrivate(pn);\n            case METHOD -> pn.method;\n            case ACCESSOR -> {\n                if (pn.getter == null) {\n                    throw JsErrorException.typeError(\"'\" + pn.name + \"' was defined without a getter\");\n                }\n                yield Interpreter.invokeGetter(pn.getter, target, context);\n            }\n        };\n    }\n\n    static void set(Object target, PrivateName pn, Object value, CoreContext context) {\n        JsObject obj = branded(target, pn, \"write\");\n        switch (pn.kind) {\n            case FIELD -> obj.putPrivate(pn, value);\n            case METHOD -> throw JsErrorException.typeError(\"Cannot write to private method \" + pn.name);\n            case ACCESSOR -> {\n                if (pn.setter == null) {\n                    throw JsErrorException.typeError(\"'\" + pn.name + \"' was defined without a setter\");\n                }\n                Interpreter.invokeSetter(pn.setter, target, value, context);\n            }\n        }","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/PrivateAccess.java#L39-L75","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nclass C { accessor set #x(v) { this._x = v; } }\nnew C().#x; // TypeError: '#x' was defined without a getter\n// after\nclass C { accessor #x; } // or: get #x() { return this._x; }","handlingStrategy":"type-guard","validationCode":"// inspect the class declaration for a getter on the private accessor\nconst hasGetter = /get\\s+#prop\\b|accessor\\s+#prop\\b/.test(classSource);","typeGuard":"function privateAccessorReadable(classSrc, name) { return new RegExp('(get\\\\s+#' + name + '|accessor\\\\s+#' + name + ')').test(classSrc); }","tryCatchPattern":"try { v = obj.#prop; } catch (e) { if (e instanceof TypeError && e.message.includes('without a getter')) { /* fall back to underlying field */ } else { throw e; } }","preventionTips":["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."],"tags":["javascript","private-fields","type-error","accessor"],"backgroundTag":"unsupported-operation","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}