{"record":{"id":"74bb3604f22d9860","repo":"karatelabs/karate","slug":"set-like-object-s-keys-is-not-callable","errorCode":null,"errorMessage":"Set-like object's 'keys' is not callable","messagePattern":"Set-like object's 'keys' is not callable","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsSetPrototype.java","lineNumber":185,"sourceCode":"            throw JsErrorException.typeError(\"Set-like object's size is not a number\");\n        }\n        // ToIntegerOrInfinity, then RangeError on negative — spec step 7.\n        long intSize;\n        if (Double.isInfinite(d)) {\n            intSize = d > 0 ? Long.MAX_VALUE : Long.MIN_VALUE;\n        } else {\n            intSize = (long) d;\n        }\n        if (intSize < 0) {\n            throw JsErrorException.rangeError(\"Set-like object's size is negative\");\n        }\n        Object hasFn = obj.getMember(\"has\", obj, cc);\n        if (!(hasFn instanceof JsCallable has)) {\n            throw JsErrorException.typeError(\"Set-like object's 'has' is not callable\");\n        }\n        Object keysFn = obj.getMember(\"keys\", obj, cc);\n        if (!(keysFn instanceof JsCallable keys)) {\n            throw JsErrorException.typeError(\"Set-like object's 'keys' is not callable\");\n        }\n        return new SetRecord(obj, intSize, has, keys);\n    }\n\n    private static JsIterator keysIter(SetRecord rec, Context ctx) {\n        return IterUtils.iteratorFromCallable(rec.keys, rec.setObj, ctx);\n    }\n\n    /** Spec normalize: -0 → +0 on values pulled from a foreign keys() iteration. */\n    private static Object normalize(Object v) {\n        return JsMap.normalizeKey(v);\n    }\n\n    /** Direct-populate a JsSet, bypassing Set.prototype.add per spec. */\n    private static void rawAdd(JsSet s, Object value) {\n        Object normalized = normalize(value);\n        // Linear-scan match for cross-Java-numeric-type SameValueZero, mirroring\n        // JsSet.has's findStoredValue logic.","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsSetPrototype.java#L167-L203","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst setLike = { size: 2, has: v => true };\nnew Set(setLike); // TypeError\n// after\nconst setLike = { size: 2, has: v => true, keys: function* () { yield 1; yield 2; } };\nnew Set(setLike);","handlingStrategy":"type-guard","validationCode":"function isSetLike(o) { return o !== null && typeof o === 'object' && typeof o.has === 'function' && typeof o.keys === 'function' && !isNaN(Number(o.size)); }","typeGuard":"const isSetLike = (o) => o instanceof Set || (typeof o === 'object' && o !== null && typeof o.keys === 'function' && typeof o.has === 'function');","tryCatchPattern":"try { return new Set(candidate); } catch (e) { if (String(e).includes('keys')) throw new TypeError('Set-like missing keys(): ' + candidate); throw e; }","preventionTips":["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"],"tags":["javascript","set","type-error","protocol"],"backgroundTag":"invalid-argument-value","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}