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

rewriteError() in wkExecutionContext converts the engine's 'Object has too long reference chain' message into a user-facing error. WebKit refuses to serialize remote objects whose reference graph is too deep to traverse, so returning such an object from page.evaluate/evaluateHandle fails serialization.

Source

Thrown at packages/playwright-core/src/server/webkit/wkExecutionContext.ts:123

    if (!handle._objectId)
      return;
    await this._session.send('Runtime.releaseObject', { objectId: handle._objectId });
  }

  shouldPrependErrorPrefix(): boolean {
    return false;
  }
}

function potentiallyUnserializableValue(remoteObject: Protocol.Runtime.RemoteObject): any {
  const value = remoteObject.value;
  const isUnserializable = remoteObject.type === 'number' && ['NaN', '-Infinity', 'Infinity', '-0'].includes(remoteObject.description!);
  return isUnserializable ? js.parseUnserializableValue(remoteObject.description!) : value;
}

function rewriteError(error: Error): Error {
  if (error.message.includes('Object has too long reference chain'))
    throw new Error('Cannot serialize result: object reference chain is too long.');
  if (!js.isJavaScriptErrorInEvaluate(error) && !isSessionClosedError(error))
    return new Error('Execution context was destroyed, most likely because of a navigation.');
  return error;
}

function renderPreview(object: Protocol.Runtime.RemoteObject): string | undefined {
  if (object.type === 'undefined')
    return 'undefined';
  if ('value' in object)
    return String(object.value);

  if (object.description === 'Object' && object.preview) {
    const tokens = [];
    for (const { name, value } of object.preview.properties!)
      tokens.push(`${name}: ${value}`);
    return `{${tokens.join(', ')}}`;
  }
  if (object.subtype === 'array' && object.preview)

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Return only serializable primitives or shallow plain objects from evaluate; project the fields you need inside the page.
  2. For DOM nodes use evaluateHandle + explicit property reads, or return specific attributes/text instead of the node graph.
  3. Break circular references before returning, or JSON-serialize inside the page and parse outside.

Example fix

// before
const x = await page.evaluate(() => window);

// after
const x = await page.evaluate(() => ({ origin: window.location.origin, ua: navigator.userAgent }));
Defensive patterns

Strategy: validation

Validate before calling

// Keep evaluate return values shallow and primitive-typed.
const summary = await page.evaluate(() => ({
  origin: location.origin,
  title: document.title,
}));

Type guard

function isSafeSerializable(v: unknown): boolean {
  if (v === null || typeof v !== 'object') return typeof v !== 'function';
  try { JSON.stringify(v); return true; } catch { return false; }
}

Try / catch

let result;
try { result = await page.evaluate(() => window); }
catch (e) {
  if (/reference chain is too long/.test(e.message))
    result = await page.evaluate(() => ({ origin: location.origin }));
  else throw e;
}

Prevention

When it happens

Trigger: Returning a deeply-nested or self-referential object from page.evaluate/evaluateHandle (e.g. returning window, a DOM node with huge subtrees, or a circular structure). Also when an evaluate returns an object whose serialized form exceeds WebKit's internal depth cap.

Common situations: Accidentally returning a live DOM node/window from evaluate instead of a primitive. Returning application state objects with long prototype chains or large arrays. Snapshotting big component trees for inspection.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/3b7603df6fceb4f1. Report an issue: GitHub.