karatelabs/karate · error · JsErrorException (typeError)
Cannot convert object to primitive value
Error message
Cannot convert object to primitive value
What it means
If an object's @@toPrimitive method returns an object (not a primitive), the spec says ToPrimitive fails with a TypeError. Terms.java enforces this: only null, undefined, primitives, or BigInt results are accepted from the exotic conversion path.
Solutions
- Return a primitive from the toPrimitive callback (string, number, or bigint)
- Unwrap boxed results: return Number(x) or String(x) instead of a boxed object
- Fall back to valueOf/toString (delete the exotic method) if a primitive result can't be produced
Example fix
// before
const o = { [Symbol.toPrimitive]: (h) => ({ value: 1 }) };
// after
const o = { [Symbol.toPrimitive]: (h) => 1 }; Defensive patterns
Strategy: try-catch
Validate before calling
const tp = obj && obj[Symbol.toPrimitive];
if (typeof tp === 'function') { const r = tp.call(obj, 'default'); if (r !== null && typeof r === 'object') throw new Error('toPrimitive must return a primitive'); } Try / catch
try { v = coerce(obj); } catch (e) { if (String(e).includes('convert object to primitive')) v = String(JSON.stringify(obj)); else throw e; } Prevention
- Always return a primitive from Symbol.toPrimitive callbacks
- Unwrap boxed Number/String results before returning
- Test custom valueOf/toString/toPrimitive implementations with + and template literals
When it happens
Trigger: An object whose Symbol.toPrimitive function returns an object/array/another object, then that object is coerced (arithmetic, `+`, comparison, template string) in karate-js.
Common situations: Returning `this` or a wrapped value from a custom toPrimitive; returning a boxed Number/String object instead of a raw primitive.
Related errors
- Cannot convert a Symbol value to a string
- Date.prototype[@@toPrimitive] called on non-object
- Symbol.toPrimitive method is not callable
- a class declaration may not be the body of
- a function declaration may not be the body of
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/cd79ed02c0e0576c.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/Terms.java:1358
// ("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);
}
/**
* Spec §7.1.1.1 OrdinaryToPrimitive — the valueOf/toString-only dispatch
* without the @@toPrimitive check. Used by {@link #toPrimitive} as the
* fallback path, and by built-in @@toPrimitive methods (e.g. Date) that
* need to invoke OrdinaryToPrimitive on themselves without re-entering
* the @@toPrimitive lookup.
*/
static Object ordinaryToPrimitive(ObjectLike ol, String hint, CoreContext context) {
String[] order = "string".equals(hint)
? new String[]{"toString", "valueOf"}
: new String[]{"valueOf", "toString"};
for (String methodName : order) {
Object fn = ol.getMember(methodName);
if (!(fn instanceof JsCallable jsc)) {View on GitHub (pinned to a22eb90246)