karatelabs/karate · error · JsErrorException (rangeError)
Exponent must be non-negative
Error message
Exponent must be non-negative
What it means
BigInt exponentiation (`**`) with a negative exponent is undefined per the JS spec (a negative BigInt power is not an integer), so Terms.java throws a RangeError instead of calling BigInteger.pow. This matches V8's 'Exponent must be non-negative'.
Solutions
- Ensure the exponent is non-negative before the operation
- Use Number arithmetic for negative exponents: `2 ** -3` gives 0.25
- Compute the positive power and divide: 1n / (2n ** 3n) if an exact BigInt reciprocal fraction is acceptable
Example fix
// before var p = baseN ** expN; // after var p = expN < 0n ? (1n / (baseN ** -expN)) : (baseN ** expN);
Defensive patterns
Strategy: validation
Validate before calling
if (exp < 0n) throw new Error('BigInt exponent must be non-negative: ' + exp); Type guard
function canBigIntPow(b, e) { return typeof b === 'bigint' && typeof e === 'bigint' && e >= 0n; } Try / catch
try { p = base ** exp; } catch (e) { if (String(e).includes('Exponent must be non-negative')) p = Number(base) ** Number(exp); else throw e; } Prevention
- Check exponent sign before BigInt **
- Use Number math for fractional or negative exponents
- Wrap math helpers with BigInt/Number assertions at their boundaries
When it happens
Trigger: Evaluating `2n ** -3n` or `base ** exp` where both are BigInt and exp < 0n in karate-js.
Common situations: Generic math helper functions written for numbers reused with BigInt operands; computing reciprocal/powers like 10n ** -n when porting numeric code.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- BigInt.prototype method called on non-BigInt
- BigInts have no unsigned right shift, use >> instead
- Cannot convert a BigInt to a number
- Cannot convert a BigInt to a number
- Cannot convert a BigInt to a number using unary +
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/d04d2b4f4add411f.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/Terms.java:809
throw JsErrorException.rangeError("Division by zero");
}
// Java BigInteger.remainder matches JS BigInt % semantics (sign follows dividend)
return narrowBigInt(((BigInteger) lhs).remainder(r));
}
double result = lhs.doubleValue() % rhs.doubleValue();
return narrow(result);
}
static Object exp(Object lhsObject, Object rhsObject, CoreContext context) {
Number lhs = toNumericOperand(lhsObject, context);
if (context != null && context.isError()) return UNDEFINED;
Number rhs = toNumericOperand(rhsObject, context);
if (context != null && context.isError()) return UNDEFINED;
if (isBigIntOp(lhs, rhs)) {
requireBothBigInt(lhs, rhs, "**");
BigInteger r = (BigInteger) rhs;
if (r.signum() < 0) {
throw JsErrorException.rangeError("Exponent must be non-negative");
}
return narrowBigInt(((BigInteger) lhs).pow(r.intValueExact()));
}
double result = Math.pow(lhs.doubleValue(), rhs.doubleValue());
return narrow(result);
}
static Object add(Object lhs, Object rhs, CoreContext context) {
// Spec evaluation of binary +: ToPrimitive both operands first (default hint),
// then string-or-number dispatch on the *primitives*. ObjectLike on either side
// is the rare case — primitives short-circuit through the existing fast path.
if (lhs instanceof ObjectLike) {
lhs = toPrimitive(lhs, "default", context);
if (context != null && context.isError()) return UNDEFINED;
}
if (rhs instanceof ObjectLike) {
rhs = toPrimitive(rhs, "default", context);
if (context != null && context.isError()) return UNDEFINED;View on GitHub (pinned to a22eb90246)