vitejs/vite · error · Error
transport must implement send and connect when invoke is not
Error message
transport must implement send and connect when invoke is not implemented
What it means
Vite's module-runner transport abstraction supports two communication styles: a high-level invoke() RPC, or low-level send()/connect() message passing. When constructing the invokeable transport wrapper, if invoke is not implemented, Vite requires both send and connect so it can build the RPC layer on top of them. A transport object that implements neither invoke nor (send+connect) cannot communicate and is rejected.
Source
Thrown at packages/vite/src/shared/moduleRunnerTransport.ts:68
const result = await transport.invoke!({
type: 'custom',
event: 'vite:invoke',
data: {
id: 'send',
name,
data,
} satisfies InvokeSendData,
} satisfies CustomPayload)
if ('error' in result) {
throw reviveInvokeError(result.error)
}
return result.result
},
}
}
if (!transport.send || !transport.connect) {
throw new Error(
'transport must implement send and connect when invoke is not implemented',
)
}
const rpcPromises = new Map<
string,
{
resolve: (data: any) => void
reject: (data: any) => void
name: string
timeoutId?: ReturnType<typeof setTimeout>
}
>()
return {
...transport,
connect({ onMessage, onDisconnection }) {
return transport.connect!({View on GitHub (pinned to 89620f09af)
Solutions
- Implement invoke on your transport (preferred for simpler integrations — a single async RPC function).
- Or implement both send(data) and connect({ onMessage, onDisconnection }) so Vite can build RPC over them.
- Use the built-in createWebSocketModuleRunnerTransport if communicating over a WebSocket, instead of a hand-rolled transport.
Example fix
// before — transport missing required methods
const transport = {
send(data) { /* ... */ },
// connect missing, invoke missing
}
// after — implement invoke for a clean RPC interface
const transport = {
async invoke({ name, data }) {
const res = await myChannel.request(name, data)
return { result: res }
},
} Defensive patterns
Strategy: validation
Validate before calling
// Validate a transport has a usable communication shape before constructing the runner
function isValidTransport(t: any): boolean {
return (
typeof t.invoke === 'function' ||
(typeof t.send === 'function' && typeof t.connect === 'function')
)
}
if (!isValidTransport(myTransport)) {
throw new Error('Transport must implement invoke, or both send and connect')
} Type guard
function isInvokeTransport(t: any): t is { invoke: Function } {
return typeof t?.invoke === 'function'
}
function isSendConnectTransport(t: any): t is { send: Function; connect: Function } {
return typeof t?.send === 'function' && typeof t?.connect === 'function'
} Prevention
- Implement invoke on custom transports for the simplest valid shape.
- Prefer createWebSocketModuleRunnerTransport for WebSocket-based runners.
- Unit-test custom transports by asserting they satisfy invoke or (send+connect).
When it happens
Trigger: Providing a ModuleRunnerTransport object to a ModuleRunner that has no invoke method and is missing either send or connect (or both). This happens when creating a custom transport for the module runner (e.g. over a worker, iframe, or custom WebSocket) and omitting required methods.
Common situations: Building a custom SSR module-runner transport for a non-WebSocket channel (worker_threads, BroadcastChannel, postMessage). Providing a partial transport object where connect was forgotten. Copying a transport shape from an older Vite version where the requirements differed.
Related errors
- invoke was called before connect
- HMR is not supported by this runner transport, but `hmr` opt
- [module runner] Failed to load "${url}"${importer ? ` import
- send was called before connect
- [module runner] Dynamic access of "import.meta.env" is not s
AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03).
Data as JSON: /data/errors/2ef4575264711adc.json.
Report an issue: GitHub.