karatelabs/karate · error · JsErrorException (typeError)

Cannot write to private method

Error message

Cannot write to private method ${pn.name}

What it means

`PrivateAccess.set` throws this TypeError when code assigns to a private method (`obj.#method = ...`). Private methods are immutable class members; the ES spec forbids writing to them, so the engine rejects the assignment instead of silently overwriting.

Solutions

  1. Remove the assignment to the private method.
  2. Change the declaration from a private method to a private field if rebinding is intended (`#run = () => {...}`).
  3. Use a different private field to hold the replacement function instead of reassigning the method name.

Example fix

// before
class C { #run() {} init() { this.#run = () => {}; } } // TypeError
// after
class C { #run = () => {}; }
Defensive patterns

Strategy: validation

Validate before calling

// ensure the target is a private field, not a method, before assigning
const isMethod = /#run\s*\(/.test(classSource);
if (isMethod) throw new Error('cannot assign to private method #run');

Type guard

function isPrivateMethod(classSrc, name) { return new RegExp('#' + name + '\\s*\\(').test(classSrc); }

Try / catch

try { c.#run = fn; } catch (e) { if (e instanceof TypeError && /Cannot write to private method/.test(e.message)) { c = new CWithField(fn); } else { throw e; } }

Prevention

When it happens

Trigger: An assignment expression whose LHS resolves to a private name of kind METHOD, e.g. `this.#run = fn` inside or against a class that declared `#run() {}` as a method.

Common situations: Attempting to monkey-patch a private method from within the class; typos where a field was intended (`#handler = ...`) but was declared as a method; refactors that converted fields to methods while old assignment code remained.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/PrivateAccess.java:68

    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);
            }
        }
    }

    private static JsObject branded(Object target, PrivateName pn, String verb) {
        if (target instanceof JsObject obj && obj.hasPrivate(pn)) {
            return obj;
        }
        throw JsErrorException.typeError("Cannot " + verb + " private member " + pn.name
                + " from an object whose class did not declare it");
    }

}

View on GitHub (pinned to a22eb90246)