karatelabs/karate · error · TypeError
Set-like object's 'keys' is not callable
Error message
Set-like object's 'keys' is not callable
What it means
This TypeError is thrown when code passes a Set-like object (to Set APIs such as union/intersection/difference) whose 'keys' property is not a callable function. The engine validates the Set-like protocol: a Set-like must expose a numeric 'size' and callable 'has' and 'keys' methods. If 'keys' is missing or non-callable, the object cannot be iterated as a Set record.
Solutions
- Add a callable keys() method to your Set-like object that returns an iterator over its elements
- Check the object's shape before passing it: typeof obj.keys === 'function'
- Use a real Set instance instead of a custom Set-like if iteration support is not needed
Example fix
// before
const setLike = { size: 2, has: v => true };
new Set(setLike); // TypeError
// after
const setLike = { size: 2, has: v => true, keys: function* () { yield 1; yield 2; } };
new Set(setLike); Defensive patterns
Strategy: type-guard
Validate before calling
function isSetLike(o) { return o !== null && typeof o === 'object' && typeof o.has === 'function' && typeof o.keys === 'function' && !isNaN(Number(o.size)); } Type guard
const isSetLike = (o) => o instanceof Set || (typeof o === 'object' && o !== null && typeof o.keys === 'function' && typeof o.has === 'function');
Try / catch
try { return new Set(candidate); } catch (e) { if (String(e).includes('keys')) throw new TypeError('Set-like missing keys(): ' + candidate); throw e; } Prevention
- When building Set-like objects, implement size, has, and keys together
- Unit-test Set-likes by passing them to new Set() before production use
- Prefer real Set instances unless lazy iteration is required
When it happens
Trigger: Passing an object with 'size' and 'has' but no 'keys', or with keys set to a non-function value, into a Set method that accepts Set-like arguments (e.g. new Set(setLike), set.union(obj), set.symmetricDifference(obj)) via getSetRecord.
Common situations: Hand-rolled Set-like objects missing one method; objects ported from another environment where 'keys' was removed or renamed; typos like 'key' instead of 'keys'; objects built dynamically where the method failed to attach.
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
- Set.prototype method called with non-object
- toBytes() argument must be a list of numbers, got
- toBytes() list must contain only numbers, got
- xmlPath() first argument must be XML node or string, but was
- NoSuchElementException
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/74bb3604f22d9860.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/JsSetPrototype.java:185
throw JsErrorException.typeError("Set-like object's size is not a number");
}
// ToIntegerOrInfinity, then RangeError on negative — spec step 7.
long intSize;
if (Double.isInfinite(d)) {
intSize = d > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;
} else {
intSize = (long) d;
}
if (intSize < 0) {
throw JsErrorException.rangeError("Set-like object's size is negative");
}
Object hasFn = obj.getMember("has", obj, cc);
if (!(hasFn instanceof JsCallable has)) {
throw JsErrorException.typeError("Set-like object's 'has' is not callable");
}
Object keysFn = obj.getMember("keys", obj, cc);
if (!(keysFn instanceof JsCallable keys)) {
throw JsErrorException.typeError("Set-like object's 'keys' is not callable");
}
return new SetRecord(obj, intSize, has, keys);
}
private static JsIterator keysIter(SetRecord rec, Context ctx) {
return IterUtils.iteratorFromCallable(rec.keys, rec.setObj, ctx);
}
/** Spec normalize: -0 → +0 on values pulled from a foreign keys() iteration. */
private static Object normalize(Object v) {
return JsMap.normalizeKey(v);
}
/** Direct-populate a JsSet, bypassing Set.prototype.add per spec. */
private static void rawAdd(JsSet s, Object value) {
Object normalized = normalize(value);
// Linear-scan match for cross-Java-numeric-type SameValueZero, mirroring
// JsSet.has's findStoredValue logic.View on GitHub (pinned to a22eb90246)