microsoft/playwright · error · Error

Resulting promise was garbage collected.

Error message

Resulting promise was garbage collected.

What it means

Rewritten in rewriteError() (crExecutionContext.ts:108) when CDP reports 'Promise was collected'. The promise returned by an evaluated async function was garbage-collected in the page before it settled, so its resolution cannot be delivered.

Source

Thrown at packages/playwright-core/src/server/chromium/crExecutionContext.ts:108

  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;
}

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

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Store the promise in a page-scope variable (e.g. window.__pending) so it stays reachable, then return it.
  2. Use page.waitForFunction with a polling predicate instead of returning a long-lived promise.
  3. Run the async work to completion inside evaluate and return only the final primitive.

Example fix

// before
const ok = await page.evaluate(() => fetch('/x').then(r => r.ok));
// after
const ok = await page.evaluate(async () => {
  const p = fetch('/x').then(r => r.ok);
  window.__pending = p;
  return await p;
});
Defensive patterns

Strategy: validation

Validate before calling

// Keep the promise reachable in page scope until it settles.
const ok = await page.evaluate(async () => {
  const p = fetch('/x').then(r => r.ok);
  (window as any).__pending = (window as any).__pending || [];
  (window as any).__pending.push(p);
  return await p;
});

Prevention

When it happens

Trigger: page.evaluate(() => someAsyncWork()) where the work loses its only reference; returning a promise that the page lets go of (fire-and-forget); awaiting on a promise stored in a weak map.

Common situations: Returning fetch().then() chains without retaining them; awaiting promises that the SPA drops after re-render; long-running tasks that lose their root reference.

Related errors


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