microsoft/playwright · error · ValidationError

${path}: object with guid ${guid} has type ${dispatcher._typ

Error message

${path}: object with guid ${guid} has type ${dispatcher._type}, expected ${names.toString()}

What it means

ValidationError raised in _tChannelImplFromWire when the guid resolves to a Dispatcher but its _type is not in the accepted names list for that parameter. The wire validator expects a specific channel type (e.g. 'Page', 'Frame', 'ElementHandle'); receiving a different but existing handle type is rejected. The message names the actual type and the expected types to pinpoint the mismatch.

Source

Thrown at packages/playwright-core/src/server/dispatchers/dispatcher.ts:250

    };
  }

  private _validatorFromWireContext(): ValidatorContext {
    return {
      tChannelImpl: this._tChannelImplFromWire.bind(this),
      binary: this._isInProcess ? 'buffer' : 'fromBase64',
      isUnderTest,
    };
  }

  private _tChannelImplFromWire(names: '*' | string[], arg: any, path: string, context: ValidatorContext): any {
    if (arg && typeof arg === 'object' && typeof arg.guid === 'string') {
      const guid = arg.guid;
      const dispatcher = this._dispatcherByGuid.get(guid);
      if (!dispatcher)
        throw new ValidationError(`${path}: no object with guid ${guid}`);
      if (names !== '*' && !names.includes(dispatcher._type))
        throw new ValidationError(`${path}: object with guid ${guid} has type ${dispatcher._type}, expected ${names.toString()}`);
      return dispatcher;
    }
    throw new ValidationError(`${path}: expected guid for ${names.toString()}`);
  }

  private _tChannelImplToWire(names: '*' | string[], arg: any, path: string, context: ValidatorContext): any {
    if (arg instanceof Dispatcher)  {
      if (names !== '*' && !names.includes(arg._type))
        throw new ValidationError(`${path}: dispatcher with guid ${arg._guid} has type ${arg._type}, expected ${names.toString()}`);
      return { guid: arg._guid };
    }
    throw new ValidationError(`${path}: expected dispatcher ${names.toString()}`);
  }

  existingDispatcher<DispatcherType>(object: any): DispatcherType | undefined {
    return this._dispatcherByObject.get(object) as DispatcherType | undefined;
  }

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Send the exact handle type the method's channel signature declares (see channels.d.ts / protocol.yml).
  2. Add type checks on the client side before sending: assert(handle._type === 'Page') or use the typed client API which encodes the constraint.
  3. Double-check refactors that touch handle plumbing — the guid is fine but the type is wrong.

Example fix

// before: sending a Page where a Frame is required
await connection.send('frame', 'click', { frame: page });

// after: pass the correct type
await connection.send('frame', 'click', { frame: page.mainFrame() });
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate handle type before sending.
function assertChannel(handle: { _type?: string }, expected: string[]) {
  if (!handle || !expected.includes(handle._type!))
    throw new Error(`Expected ${expected.join('|')}, got ${handle?._type}`);
}

Type guard

function isChannelOf<T extends { _type: string }>(h: any, types: readonly string[]): h is T {
  return !!h && typeof h._type === 'string' && types.includes(h._type);
}

Prevention

When it happens

Trigger: Passing a Page where a Frame is required, an ElementHandle where a JSHandle is required, a BrowserType where a Browser is required, etc. — i.e. a valid handle of the wrong channel type. Common with hand-rolled protocol clients or dynamic dispatch that picks the wrong handle variable.

Common situations: Custom protocol/transport code that confuses similar handle types (Page vs Frame, ElementHandle vs JSHandle, Request vs Response); refactors that change a field but leave the wrong handle being sent.

Related errors


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