microsoft/playwright · error · ValidationError

${path}: expected dispatcher ${names.toString()}

Error message

${path}: expected dispatcher ${names.toString()}

What it means

ValidationError raised in _tChannelImplToWire when a result field declared as a channel is being serialized from a value that is not a Dispatcher instance. The outbound serializer expects an actual Dispatcher object; receiving anything else (plain object, undefined, null, a raw guid string) means server code returned the wrong shape for a channel field.

Source

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

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

  registerDispatcher(dispatcher: DispatcherScope) {
    assert(!this._dispatcherByGuid.has(dispatcher._guid));
    this._dispatcherByGuid.set(dispatcher._guid, dispatcher);
    this._dispatcherByObject.set(dispatcher._object, dispatcher);
    let list = this._dispatchersByBucket.get(dispatcher._gcBucket);
    if (!list) {
      list = new Set();
      this._dispatchersByBucket.set(dispatcher._gcBucket, list);
    }
    list.add(dispatcher._guid);
  }

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. If modifying Playwright: return the proper Dispatcher (e.g. new PageDispatcher(...)) for every channel result field; rebuild to regenerate validators.
  2. Run npm run flint / watch so generated code matches protocol.yml.
  3. End users: this signals a Playwright bug — file an issue with a repro.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await connection.dispatch(...);
} catch (e) {
  if (/expected dispatcher/.test(e.message)) {
    throw new Error('Playwright internal: a channel result field was not a Dispatcher — rebuild/regenerate');
  }
  throw e;
}

Prevention

When it happens

Trigger: Server-side method returns a non-Dispatcher (undefined, plain object, or a guid) for a result field whose validator requires a channel. As with error 276, this is internal to Playwright and typically follows a code/protocol edit that broke the return shape.

Common situations: Editing Playwright internals (returning undefined from a dispatcher method, forgetting to wrap an object in its Dispatcher); stale generated validators after protocol changes.

Related errors


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