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
- Pass a live object of the correct type from the same connection.
- Verify the value is not null/undefined and was obtained from the current connection.
- 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
- Never pass objects across separate Playwright connections.
- Obtain channel arguments from the same browser/connection lifecycle that will use them.
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
- Object with guid ${arg.guid} was not bound in the connection
- ${path}: expected channel ${names.toString()}
- CDP session must be initiated with either Page or Frame, not
- ${path}: no object with guid ${guid}
- ${path}: object with guid ${guid} has type ${dispatcher._typ
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/6648e01737f618a5.
Report an issue: GitHub.