microsoft/playwright · error

Only element handles can be evaluated in the

Error message

Only element handles can be evaluated in the "${params.world}" world

What it means

JSHandleDispatcher.evaluateExpression throws when a world ('main' or 'utility') is requested for an expression evaluated on a plain JSHandle. Execution worlds only make sense for ElementHandle, whose element can be moved into a specific world; the base JSHandle dispatcher unconditionally rejects params.world.

Solutions

  1. Omit the world option when evaluating on a plain JSHandle.
  2. Use locator.evaluate or elementHandle.evaluateExpression with the world option instead — worlds are only supported for element handles.
  3. If you need the value in a specific world, evaluate on the page/frame and pass the handle as an argument.

Example fix

// before
const handle = await page.evaluateHandle('window.foo');
await handle.evaluateExpression('el => el', { world: 'utility' });
// after
await page.locator('#target').evaluate('el => el', undefined, { world: 'utility' });
Defensive patterns

Strategy: type-guard

Type guard

function canUseWorld(handle: unknown): handle is import('playwright-core').ElementHandle {
  return !!handle && typeof (handle as any).evaluateExpression === 'function' && (handle as any)._element !== undefined;
}
// simpler: only pass world when the subject is a Locator / ElementHandle

Try / catch

try {
  return await handle.evaluateExpression(expr, { world: 'utility' });
} catch (e) {
  if (String(e.message).includes('Only element handles')) return await handle.evaluateExpression(expr);
  throw e;
}

Prevention

When it happens

Trigger: Calling jsHandle.evaluateExpression(expression, { world: 'utility' }) or passing a world option through an evaluate call routed at a non-element handle (e.g. a page.evaluateHandle result).

Common situations: Reusing an evaluate helper written for locators/element handles and passing a plain JSHandle; adding world options to generic handle evaluation for isolation reasons.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of microsoft/playwright@f1d33b5029 (2026-09-21). Data as JSON: /api/errors/7ed38c20b2665a17. Report an issue: GitHub.

Appendix: source

Thrown at packages/playwright-core/src/server/dispatchers/jsHandleDispatcher.ts:47

export class JSHandleDispatcher<ParentScope extends JSHandleDispatcherParentScope = JSHandleDispatcherParentScope> extends Dispatcher<js.JSHandle, channels.JSHandleChannel, ParentScope> implements channels.JSHandleChannel {
  _type_JSHandle = true;

  static fromJSHandle(scope: JSHandleDispatcherParentScope, handle: js.JSHandle): JSHandleDispatcher {
    return scope.connection.existingDispatcher<JSHandleDispatcher>(handle) || new JSHandleDispatcher(scope, handle);
  }

  protected constructor(scope: ParentScope, jsHandle: js.JSHandle) {
    // Do not call this directly, use createHandle() instead.
    super(scope, jsHandle, jsHandle.asElement() ? 'ElementHandle' : 'JSHandle', {
      preview: jsHandle.toString(),
    });
    jsHandle._setPreviewCallback(preview => this._dispatchEvent('previewUpdated', { preview }));
  }

  async evaluateExpression(params: channels.JSHandleEvaluateExpressionParams, progress: Progress): Promise<channels.JSHandleEvaluateExpressionResult> {
    if (params.world)
      throw new Error(`Only element handles can be evaluated in the "${params.world}" world`);
    const jsHandle = await this._object.evaluateExpression(progress, params.expression, { isFunction: params.isFunction, serialize: params.serialize }, parseArgument(params.arg));
    return { value: serializeResult(jsHandle, params) };
  }

  async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams, progress: Progress): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
    const jsHandle = await this._object.evaluateExpressionHandle(progress, params.expression, { isFunction: params.isFunction, serialize: params.serialize }, parseArgument(params.arg));
    // If "jsHandle" is an ElementHandle, it belongs to the same frame as "this".
    return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope() as FrameDispatcher, jsHandle) };
  }

  async getProperty(params: channels.JSHandleGetPropertyParams, progress: Progress): Promise<channels.JSHandleGetPropertyResult> {
    const jsHandle = await this._object.getProperty(progress, params.name);
    // If "jsHandle" is an ElementHandle, it belongs to the same frame as "this".
    return { handle: ElementHandleDispatcher.fromJSOrElementHandle(this.parentScope() as FrameDispatcher, jsHandle) };
  }

  async getPropertyList(params: channels.JSHandleGetPropertyListParams, progress: Progress): Promise<channels.JSHandleGetPropertyListResult> {
    const map = await this._object.getProperties(progress);

View on GitHub (pinned to f1d33b5029)