microsoft/playwright · error · Error
Cannot serialize result: object reference chain is too long.
Error message
Cannot serialize result: object reference chain is too long.
What it means
Rewritten in rewriteError() (crExecutionContext.ts:104) when the underlying CDP error message contains 'Object reference chain is too long' or 'CBOR: stack limit exceeded'. The protocol serializer hit its reference-depth limit while materializing the result of an evaluate.
Source
Thrown at packages/playwright-core/src/server/chromium/crExecutionContext.ts:104
result.set(property.name, createHandle(object._context, property.value));
}
return result;
}
async releaseHandle(handle: js.JSHandle): Promise<void> {
if (!handle._objectId)
return;
await releaseObject(this._client, handle._objectId);
}
shouldPrependErrorPrefix(): boolean {
return false;
}
}
function rewriteError(error: Error): Protocol.Runtime.evaluateReturnValue {
if (error.message.includes('Object reference chain is too long') || error.message.includes('CBOR: stack limit exceeded'))
throw new Error('Cannot serialize result: object reference chain is too long.');
if (error.message.includes('Object couldn\'t be returned by value'))
return { result: { type: 'undefined' } };
if (error.message.includes('Promise was collected'))
throw new Error('Resulting promise was garbage collected.');
if (error instanceof TypeError && error.message.startsWith('Converting circular structure to JSON'))
rewriteErrorMessage(error, error.message + ' Are you passing a nested JSHandle?');
if (!js.isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error))
throw new Error('Execution context was destroyed, most likely because of a navigation.');
throw error;
}
function potentiallyUnserializableValue(remoteObject: Protocol.Runtime.RemoteObject): any {
const value = remoteObject.value;
const unserializableValue = remoteObject.unserializableValue;
return unserializableValue ? js.parseUnserializableValue(unserializableValue) : value;
}
View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Project the data to a small/plain shape inside the page before returning.
- Return a handle via page.evaluateHandle(() => big) instead of by-value evaluate.
- If you need fields, read them individually with handle.getProperty and jsonValue on primitives.
Example fix
// before
const state = await page.evaluate(() => window.__bigStore__);
// after
const handle = await page.evaluateHandle(() => window.__bigStore__);
const summary = await handle.evaluate(s => ({ users: s.users.length, ready: s.ready })); Defensive patterns
Strategy: validation
Validate before calling
// Project to a small shape inside the page; fall back to a handle for large graphs.
const small = await page.evaluate(() => {
const s = (window as any).__bigStore__;
return { count: s?.items?.length ?? 0, ready: !!s?.ready };
});
// Only if you really need the whole object:
const handle = await page.evaluateHandle(() => (window as any).__bigStore__); Prevention
- Never return window/document/large framework state by value from evaluate.
- Prefer evaluateHandle for big objects and read fields individually.
- Strip to JSON-safe primitives before crossing the protocol boundary.
When it happens
Trigger: page.evaluate(() => hugeOrDeeplyNestedObject); returning window, document, a framework's component tree, or a cyclic graph by value; jsHandle.getProperty chains projected back as values.
Common situations: Returning the whole Vue/React root from evaluate; returning large state stores; debugging helpers that dump nested configs.
Related errors
- Cannot serialize result: object reference chain is too long.
- Could not connect to ${channel}.\n${remoteDebuggingHint(chan
- Playwright manages remote debugging connection itself.
- Connecting to ${channel} by channel name is not supported on
- Could not connect to ${channel}: DevToolsActivePort file not
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/1f3c82f6fee65060.
Report an issue: GitHub.