microsoft/playwright · error · ValidationError

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

Error message

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

What it means

Thrown by the marshalling validator tChannelImplToWire() when an argument expected to be a Channel (a Playwright remote object reference) is not a ChannelOwner of the right type. The validator checks `arg._object instanceof ChannelOwner` and that its _type is in the allowed names list ('*' accepts any); otherwise it raises a ValidationError naming the path and expected channel types.

Source

Thrown at packages/playwright-core/src/client/channelOwner.ts:244

    // which can cause jest to crash. Let's help it out
    // by just returning the important values.
    return {
      _type: this._type,
      _guid: this._guid,
    };
  }
}

function logApiCall(logger: Logger | undefined, message: string) {
  if (logger && logger.isEnabled('api', 'info'))
    logger.log('api', 'info', message, [], { color: 'cyan' });
  debugLogger.log('api', message);
}

function tChannelImplToWire(names: '*' | string[], arg: any, path: string, context: ValidatorContext) {
  if (arg._object instanceof ChannelOwner && (names === '*' || names.includes(arg._object._type)))
    return { guid: arg._object._guid };
  throw new ValidationError(`${path}: expected channel ${names.toString()}`);
}

type ApiZone = {
  apiName: string;
  frames: channels.StackFrame[];
  title?: string;
  internal?: boolean;
  reported: boolean;
  userData: any;
  stepId?: string;
  error?: Error;
};

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Pass a live object of the correct type from the same connection.
  2. Verify the value is not null/undefined and was obtained from the current connection.
  3. Use the public API rather than direct channel-passing helpers.

Example fix

// before
await someApiCall({ frame: staleOrWrongObject });
// after
const frame = page.mainFrame(); // correct type, same connection
await someApiCall({ frame });
Defensive patterns

Strategy: type-guard

Validate before calling

function isLiveChannel(arg: any, type?: string): boolean {
  return !!arg?._object
    && arg._object instanceof (require('./channelOwner').ChannelOwner)
    && (!type || arg._object._type === type);
}
if (!isLiveChannel(value, 'Frame'))
  throw new Error('Expected a live Frame channel from the same connection.');

Type guard

function isChannelOwnerOf<T extends string>(arg: any, type: T): boolean {
  return !!arg?._object && arg._object._type === type;
}

Prevention

When it happens

Trigger: Passing null, undefined, a plain object, a wrong-type channel (e.g. a Page where a Frame channel is required), or an object from a different Playwright connection into any API whose parameter is declared as tChannel in the protocol.

Common situations: Cross-connection object passing (two browsers/connections in one process); stale references after a close/b navigation; serializing/deserializing objects that lose their _object binding; calling internal protocol methods directly.

Related errors


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