karatelabs/karate · error · JsErrorException (typeError)

Cannot convert a Symbol value to a string

Error message

Cannot convert a Symbol value to a string

What it means

Symbols are primitives, but per spec §7.1.1 they cannot be coerced to string by ordinary conversions (no OrdinaryToPrimitive path). Terms.java's ToPrimitive throws a TypeError whenever a JsSymbol reaches generic coercion, so expressions like `sym + ''` or `String(sym)` via implicit coercion fail.

Solutions

  1. Call String(sym) explicitly if you truly want the text (explicit Symbol-to-string via String() is allowed; this error concerns implicit coercion paths in this implementation)
  2. Avoid using Symbol values in concatenation; store their description: sym.description
  3. Use a string key instead of a Symbol when ordinary stringification is needed

Example fix

// before
var label = 'key: ' + sym;
// after
var label = 'key: ' + sym.description;
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof v === 'symbol') throw new Error('cannot coerce Symbol to string implicitly');

Type guard

function isStringCoercible(v) { return typeof v !== 'symbol'; }

Try / catch

try { s = prefix + v; } catch (e) { if (String(e).includes('Symbol value to a string')) s = prefix + String(v); else throw e; }

Prevention

When it happens

Trigger: Using a Symbol value in string concatenation (`'' + sym`), template literals in some paths, or any arithmetic/string coercion where Terms.toPrimitive receives a JsSymbol.

Common situations: Using built-in symbol keys (@@iterator, @@toPrimitive) as ordinary property values; logging or stringifying a value that turned out to be a Symbol extracted from an object.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

     * wins; if both return objects, throws TypeError.
     * <p>
     * Errors raised by {@code valueOf} / {@code toString} flow through the supplied
     * {@code context} (same pattern as {@link #toStringCoerce}); callers must check
     * {@code context.isError()} after invoking. When error state is set, returns
     * {@link #UNDEFINED} as a placeholder — the caller should bail.
     * <p>
     * Hot-path note: every call site already had to dispatch on type for primitives;
     * this method only enters the ObjectLike branch on the rare case where the input
     * is genuinely an object.
     */
    static Object toPrimitive(Object value, String hint, CoreContext context) {
        if (value == null || value == UNDEFINED) {
            return value;
        }
        // §7.1.1: a symbol IS a primitive, so it never runs OrdinaryToPrimitive.
        // Every arithmetic / string coercion that reaches it throws (`sym + ''`).
        if (value instanceof JsSymbol) {
            throw JsErrorException.typeError("Cannot convert a Symbol value to a string");
        }
        // Boxed primitives unwrap directly — equivalent to spec valueOf for these,
        // but cheaper than a method dispatch.
        if (value instanceof JsPrimitive jp) {
            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

View on GitHub (pinned to a22eb90246)