puppeteer/puppeteer · error · Error
Trying to evaluate JSHandle from different global types. Usu
Error message
Trying to evaluate JSHandle from different global types. Usually this means you're using a handle from a worker in a page or vice versa.
What it means
Thrown by Realm.serialize() when a JSHandle/ElementHandle being passed as an evaluation argument belongs to a realm of a different global type than the target realm — specifically when one is a BidiFrameRealm (window) and the other is a BidiWorkerRealm (worker), or one is frame and the other worker. Cross-global-type handle passing is impossible because workers and pages have disjoint object graphs.
Source
Thrown at packages/puppeteer-core/src/bidi/Realm.ts:246
}
return BidiJSHandle.from(result, this);
}
async serializeAsync(arg: unknown): Promise<Bidi.Script.LocalValue> {
if (arg instanceof LazyArg) {
arg = await arg.get(this);
}
return this.serialize(arg);
}
serialize(arg: unknown): Bidi.Script.LocalValue {
if (arg instanceof BidiJSHandle || arg instanceof BidiElementHandle) {
if (arg.realm !== this) {
if (
!(arg.realm instanceof BidiFrameRealm) ||
!(this instanceof BidiFrameRealm)
) {
throw new Error(
"Trying to evaluate JSHandle from different global types. Usually this means you're using a handle from a worker in a page or vice versa.",
);
}
if (arg.realm.environment !== this.environment) {
throw new Error(
"Trying to evaluate JSHandle from different frames. Usually this means you're using a handle from a page on a different page.",
);
}
}
if (arg.disposed) {
throw new Error('JSHandle is disposed!');
}
return arg.remoteValue() as Bidi.Script.RemoteReference;
}
return BidiSerializer.serialize(arg);
}
View on GitHub (pinned to d484e21c17)
Solutions
- Re-acquire the handle inside the correct realm: serialize the value to a primitive in the source realm and pass that, or re-query the DOM in the page.
- For DOM access from a worker, message-pass node data via postMessage rather than passing an ElementHandle.
- Confirm both handles share the same realm type before combining them in one evaluate.
- Use `worker.evaluateHandle(...)` to create handles native to the worker realm.
Example fix
// before const handle = await page.evaluateHandle(() => window); await worker.evaluate(h => h.location, handle); // throws: different global types // after await worker.evaluate(() => self.location);
Defensive patterns
Strategy: type-guard
Validate before calling
// Compare realm kinds before cross-passing handles (uses public frame mainRealm). const sameRealmKind = (a: any, b: any) => !!a && !!b && a?.constructor?.name === b?.constructor?.name;
Type guard
// Heuristic: element/JS handles from workers come via WebWorker; page handles via Page. const isWorkerHandle = (h: any): boolean => !!h?.realm && /Worker/.test(h.realm?.constructor?.name ?? '');
Try / catch
try {
await worker.evaluate(fn, pageHandle);
} catch (e) {
if (/different global types/.test(String(e?.message))) {
// re-acquire or pass primitives instead
} else { throw e; }
} Prevention
- Never pass page handles into worker evaluates; pass primitives.
- Acquire handles in the same realm that will consume them.
- Use postMessage patterns to share data between page and worker.
When it happens
Trigger: Passing a handle obtained from `page.evaluateHandle(...)` (frame realm) into `worker.evaluate(...)` or vice versa; passing an ElementHandle from the page into a WebWorker's evaluate; calling `worker.evaluate(fn, pageHandle)`.
Common situations: Sharing handles across page and worker in test harnesses; refactoring that moved an evaluateHandle call from page to worker without re-acquiring the handle; assuming workers can see page DOM directly.
Related errors
- Trying to evaluate JSHandle from different frames. Usually t
- Cannot adopt DOM nodes into a worker.
- JSHandle is disposed!
- Unable to serializable ${typeof arg}
- Touch has already started
AI-assisted analysis of puppeteer/puppeteer@d484e21c17 (2026-08-12).
Data as JSON: /api/errors/3a5d486289916e6f.
Report an issue: GitHub.