karatelabs/karate · error · JsErrorException
DataCloneError: a could not be cloned
Error message
DataCloneError: a ${type} could not be cloned What it means
The structured clone algorithm (JsStructuredClone) cannot copy values of type `function` or `symbol`, matching the HTML spec's DataCloneError. When clone() encounters such a value via typeOf it throws "DataCloneError: a ${type} could not be cloned". This occurs in postMessage-style messaging, worker-style APIs, or any structured-clone copy operation.
Solutions
- Remove functions/symbols from the data being cloned; send plain data (objects, arrays, primitives).
- Pass function references separately (e.g. by name) and resolve them on the receiving side.
- Convert symbol-keyed data to string keys before cloning.
Example fix
// before
channel.postMessage({ callback: function () { ... } });
// after
channel.postMessage({ callbackName: 'myHandler' }); // resolve by name on receiver Defensive patterns
Strategy: type-guard
Validate before calling
// before cloning, assert the payload is data-only
function isCloneable(v) {
return v === null || ['string','number','boolean','undefined'].includes(typeof v)
|| (Array.isArray(v) && v.every(isCloneable))
|| (typeof v === 'object' && Object.values(v).every(isCloneable));
} Type guard
function isCloneable(v) {
if (typeof v === 'function' || typeof v === 'symbol') return false;
if (v === null || typeof v !== 'object') return true;
return Object.values(v).every(isCloneable);
} Prevention
- Treat message payloads as plain data (POJOs): no functions, symbols, or class instances with methods.
- Pass functions by name/reference separately from cloned data.
- Add a validation step that walks the payload before postMessage/clone.
When it happens
Trigger: Calling the structured-clone entry (clone/call/copyOwn) with an argument that is a function or a symbol, or an object graph containing one (e.g. passing a callback through a message).
Common situations: Passing callbacks or class methods through message passing APIs; including symbols as object keys in payloads intended for cloning; porting Node/browser structuredClone code that hit the same rule.
Related errors
- a class declaration may not be the body of
- a function declaration may not be the body of
- a lexical declaration may not be the body of
- AggregateError requires an iterable of errors
- append() needs at least two arguments
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/0060f4f53d054046.
Report an issue: GitHub.
Appendix: source
Thrown at karate-js/src/main/java/io/karatelabs/js/JsStructuredClone.java:65
private static final byte DATA_ATTRS = JsObject.WRITABLE | JsObject.CONFIGURABLE;
private JsStructuredClone() {
}
static Object call(Context context, Object[] args) {
if (args.length == 0) {
throw JsErrorException.typeError("structuredClone requires at least 1 argument");
}
// args[1] is the options bag carrying `transfer` — nothing in this
// engine is transferable, so it is accepted and ignored
return clone(args[0], new IdentityHashMap<>(), context instanceof CoreContext cc ? cc : null);
}
private static Object clone(Object value, IdentityHashMap<Object, Object> memo, CoreContext ctx) {
String type = Terms.typeOf(value);
if ("function".equals(type) || "symbol".equals(type)) {
throw dataCloneError(type);
}
Object seen = memo.get(value);
if (seen != null) {
return seen;
}
if (value instanceof JsPrimitive jp) {
return remember(memo, value, boxed(jp));
}
if (value instanceof JsDate d) {
return remember(memo, value, new JsDate(d.getTimeValue()));
}
if (value instanceof JsRegex r) {
return remember(memo, value, new JsRegex(r.pattern, r.flags));
}
if (value instanceof JsArray a) {
JsArray copy = new JsArray(new ArrayList<>(a.list.size()));
memo.put(value, copy);
// jsEntries is the spec own-key walk: holes are skipped, indexView on GitHub (pinned to a22eb90246)