microsoft/playwright · error · JavaScriptErrorInEvaluate

JSHandles can be evaluated only in the context they were cre

Error message

JSHandles can be evaluated only in the context they were created!

What it means

Thrown as a JavaScriptErrorInEvaluate by evaluateExpression() when a JSHandle being passed as an argument belongs to a different ExecutionContext than the one the evaluate is running in. Each frame (and each isolated world) has its own context; handles created in one cannot be referenced in another. The check compares handle._context against the target context.

Source

Thrown at packages/playwright-core/src/server/javascript.ts:280

  args = args.map(arg => serializeAsCallArgument(arg, handle => {
    if (handle instanceof JSHandle) {
      if (!handle._objectId)
        return { fallThrough: handle._value };
      if (handle._disposed)
        throw new JavaScriptErrorInEvaluate('JSHandle is disposed!');
      const adopted = context.adoptIfNeeded(handle);
      if (adopted === null)
        return { h: pushHandle(Promise.resolve(handle)) };
      toDispose.push(adopted);
      return { h: pushHandle(adopted) };
    }
    return { fallThrough: handle };
  }));

  const utilityScriptObjects: JSHandle[] = [];
  for (const handle of await Promise.all(handles)) {
    if (handle._context !== context)
      throw new JavaScriptErrorInEvaluate('JSHandles can be evaluated only in the context they were created!');
    utilityScriptObjects.push(handle);
  }

  // See UtilityScript for arguments.
  const utilityScriptValues = [options.isFunction, options.returnByValue, expression, args.length, ...args];

  const script = `(utilityScript, ...args) => utilityScript.evaluate(...args)`;
  try {
    return await context._evaluateWithArguments(script, options.returnByValue || false, utilityScriptValues, utilityScriptObjects);
  } finally {
    toDispose.map(handlePromise => handlePromise.then(handle => handle.dispose()));
  }
}

export function parseUnserializableValue(unserializableValue: string): any {
  if (unserializableValue === 'NaN')
    return NaN;
  if (unserializableValue === 'Infinity')

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Create the JSHandle in the same frame/context where it will be used in evaluate.
  2. Re-query elements in the target frame instead of passing handles across frames.
  3. Extract serializable values (strings, numbers) from the handle before crossing context boundaries: const value = await handle.jsonValue().
  4. For cross-frame operations, use element handles' own evaluate methods which run in the element's context.

Example fix

// before
const handleFromFrameA = await frameA.evaluateHandle('document');
await frameB.evaluate(h => h.title, handleFromFrameA); // throws [335]

// after
const title = await frameA.evaluate(() => document.title);
// use the serializable value in frameB if needed
Defensive patterns

Strategy: validation

Validate before calling

// Extract serializable value before crossing context boundaries
const value = await handle.jsonValue();
// Now pass the plain value instead of the handle

Try / catch

try {
  await frame.evaluate(fn, handle);
} catch (e) {
  if (e.message.includes('only in the context they were created')) {
    const json = await handle.jsonValue();
    return frame.evaluate(fn, json);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a JSHandle from Frame A's context to an evaluate on Frame B. Using a handle from the main world in an evaluate running in the utility world. Using a handle from a previous page in the current page's evaluate. Cross-origin iframe handles used in the parent frame's evaluate.

Common situations: Storing a handle from one frame and using it in another frame's evaluate. Handle created before a navigation that created a new context. Mixing handles from addInitScript-injected scripts with main-context evaluates. Using a handle from an isolated world (e.g., from a selector engine) in a regular evaluate.

Related errors


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