karatelabs/karate · error · JsErrorException (typeError)

Symbol.toPrimitive method is not callable

Error message

Symbol.toPrimitive method is not callable

What it means

When an object defines @@toPrimitive, the spec's GetMethod requires it to be callable. Terms.java throws this TypeError when Symbol.toPrimitive exists but is not a function (e.g. a number, string, or plain object), instead of ignoring it.

Solutions

  1. Make @@toPrimitive a function: { [Symbol.toPrimitive]: (hint) => ... }
  2. Remove the property if custom conversion isn't intended, letting valueOf/toString handle coercion
  3. Guard in library code: check typeof obj[Symbol.toPrimitive] === 'function' before relying on it

Example fix

// before
const o = { [Symbol.toPrimitive]: '42' };
// after
const o = { [Symbol.toPrimitive]: (hint) => '42' };
Defensive patterns

Strategy: validation

Validate before calling

const tp = obj[Symbol.toPrimitive];
if (tp !== undefined && typeof tp !== 'function') throw new Error('Symbol.toPrimitive must be callable');

Type guard

function hasCallableToPrimitive(o) { return o == null || typeof o[Symbol.toPrimitive] === 'function' || o[Symbol.toPrimitive] === undefined; }

Try / catch

try { n = obj + 0; } catch (e) { if (String(e).includes('not callable')) n = Number(obj.valueOf ? obj.valueOf() : obj); else throw e; }

Prevention

When it happens

Trigger: An object in karate-js has a non-callable `Symbol.toPrimitive` member and is then used in arithmetic, comparison, or string coercion that triggers ToPrimitive with a hint.

Common situations: Typos like `{ [Symbol.toPrimitive]: 'number' }`, assigning the symbol itself instead of a function, or JSON/config data accidentally overwriting the well-known symbol property.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/Terms.java:1345

            return jp.getJavaValue();
        }
        if (value instanceof BigInteger || isPrimitive(value)) {
            return value;
        }
        // ObjectLike (or Java-native types we wrap): run OrdinaryToPrimitive.
        ObjectLike ol = (value instanceof ObjectLike) ? (ObjectLike) value : toObjectLike(value);
        if (ol == null || context == null) {
            // No prototype dispatch possible — return as-is and let the caller cope.
            return value;
        }
        // Spec: @@toPrimitive (the well-known Symbol.toPrimitive method) takes precedence
        // over OrdinaryToPrimitive's valueOf/toString dispatch. Hint passed verbatim
        // ("string" | "number" | "default"). Result must be a primitive; an object result
        // is a TypeError per spec. Set-but-not-callable is also a TypeError (GetMethod).
        Object exotic = ol.getMember("@@toPrimitive");
        if (exotic != null && exotic != UNDEFINED) {
            if (!(exotic instanceof JsCallable jsc)) {
                throw JsErrorException.typeError("Symbol.toPrimitive method is not callable");
            }
            CoreContext callCtx = new CoreContext(context, null, null);
            callCtx.thisObject = ol;
            String hintArg = hint == null ? "default" : hint;
            Object r = jsc.call(callCtx, new Object[]{hintArg});
            if (callCtx.isError()) {
                context.updateFrom(callCtx);
                return UNDEFINED;
            }
            if (r == null || r == UNDEFINED || isPrimitive(r) || r instanceof BigInteger) {
                return r;
            }
            throw JsErrorException.typeError("Cannot convert object to primitive value");
        }
        return ordinaryToPrimitive(ol, hint, context);
    }

    /**

View on GitHub (pinned to a22eb90246)