karatelabs/karate · error · JsErrorException

Cannot convert a BigInt to a number using unary +

Error message

Cannot convert a BigInt to a number using unary +

What it means

Thrown when unary `+` is applied to a BigInt value. Per the ECMAScript spec, unary plus performs ToNumber, and there is deliberately no implicit BigInt-to-Number conversion (it would lose precision), so `+bigint` is a TypeError.

Solutions

  1. Use `Number(bigint)` if an explicit (possibly lossy) conversion is intended.
  2. Keep the value as BigInt and use BigInt arithmetic instead of coercing to number.
  3. If string coercion was the goal, use `bigint.toString()`.

Example fix

// before
var n = +bigValue;
// after
var n = Number(bigValue); // explicit, or keep as BigInt
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof v === 'bigint') throw new Error('unary + not allowed on BigInt');

Type guard

function isBigInt(v) { return typeof v === 'bigint' || v instanceof java.math.BigInteger; }

Try / catch

try { n = +value; } catch (e) { n = Number(value); }

Prevention

When it happens

Trigger: Evaluating `+bigintVar` in Karate JS — e.g. `+10n`, `+someBigIntResult` from arithmetic or JSON fields holding big integers.

Common situations: Code that coerces numbers with unary `+` (a common idiom for strings) encountering BigInt values produced by large-integer parsing or BigInt literals.

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/bc4cdb6d43675e41. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/Interpreter.java:2457

    private static Object evalMathPreExpr(Node node, CoreContext context) {
        Node exprNode = node.get(1).getFirst();
        return switch (node.get(0).token.type) {
            case PLUS_PLUS -> PropertyAccess.preIncDec(exprNode, context, true);
            case MINUS_MINUS -> PropertyAccess.preIncDec(exprNode, context, false);
            case MINUS -> {
                Object v = eval(exprNode, context);
                // Rare path: BigInt unary negation needs to stay in the BigInt domain.
                // The plain-Number case below would TypeError because rhs (-1) is Integer.
                if (v instanceof java.math.BigInteger bi) {
                    yield Terms.narrowBigInt(bi.negate());
                }
                yield Terms.mul(v, -1, context);
            }
            case PLUS -> {
                Object v = eval(exprNode, context);
                // Spec: unary + on BigInt is a TypeError (no implicit BigInt → Number).
                if (v instanceof java.math.BigInteger) {
                    throw JsErrorException.typeError("Cannot convert a BigInt to a number using unary +");
                }
                // Spec ToNumber routes through ToPrimitive (hint=number) for
                // objects, so {valueOf: () => 7} returns 7 not NaN.
                yield Terms.toNumberCoerce(v, context);
            }
            default -> throw new RuntimeException("unexpected operator: " + node.getFirst());
        };
    }

    private static Object evalProgram(Node node, CoreContext context) {
        // A top-level "use strict" directive makes the whole script strict, and
        // must be resolved before hoisting so function objects created here
        // capture the right lexical strictness via their declaredContext.
        if (!context.strict && hasUseStrictDirective(node)) {
            context.strict = true;
        }
        // Program-scope slot frame: dense storage for let/const confined to
        // nested blocks / for-inits at top level (SlotTable.forProgram — the

View on GitHub (pinned to a22eb90246)