vitejs/vite · error · SendBeforeConnectError
invoke was called before connect
Error message
invoke was called before connect
What it means
Symmetric to the send-before-connect guard: the normalized transport's invoke() also requires isConnected to be true. If invoke is called before connect() has completed (and no connect is pending), Vite throws SendBeforeConnectError('invoke was called before connect') because the RPC layer has no channel to carry the request/response.
Source
Thrown at packages/vite/src/shared/moduleRunnerTransport.ts:250
: {}),
async send(data) {
if (!invokeableTransport.send) return
if (!isConnected) {
if (connectingPromise) {
await connectingPromise
} else {
throw new SendBeforeConnectError('send was called before connect')
}
}
await invokeableTransport.send(data)
},
async invoke(name, data) {
if (!isConnected) {
if (connectingPromise) {
await connectingPromise
} else {
throw new SendBeforeConnectError('invoke was called before connect')
}
}
return invokeableTransport.invoke(name, data)
},
}
}
export class SendBeforeConnectError extends Error {
constructor(message: string) {
super(message)
this.name = 'SendBeforeConnectError'
}
}
export const createWebSocketModuleRunnerTransport = (options: {
// eslint-disable-next-line n/no-unsupported-features/node-builtins
createConnection: () => WebSocket
pingInterval?: numberView on GitHub (pinned to 89620f09af)
Solutions
- Ensure transport.connect() is awaited before the module runner imports anything that triggers invoke.
- Let the ModuleRunner own connection lifecycle rather than invoking manually before connect.
- Make the underlying channel open synchronously where possible, or await its readiness in connect().
Example fix
// before — invoking before connect resolves
const transport = normalizeModuleRunnerTransport(asyncTransport)
const result = await transport.invoke('fetchModule', ['id']) // throws
// after — connect, then invoke
const transport = normalizeModuleRunnerTransport(asyncTransport)
await transport.connect()
const result = await transport.invoke('fetchModule', ['id']) Defensive patterns
Strategy: validation
Validate before calling
// Ensure connect resolves before invoking RPC
await transport.connect()
const result = await transport.invoke('fetchModule', ['id']) Try / catch
import { SendBeforeConnectError } from 'vite/module-runner'
try {
await transport.invoke(name, data)
} catch (e) {
if (e instanceof SendBeforeConnectError) {
await transport.connect()
return await transport.invoke(name, data)
}
throw e
} Prevention
- Await connect() before any invoke call.
- Defer module imports until after the transport connection is established.
- Await the channel's open/readiness inside connect() so isConnected is accurate.
When it happens
Trigger: Calling transport.invoke(name, data) on a normalized module-runner transport before connect() resolves. This happens when the module runner issues an RPC (e.g. fetchModule) during construction or before the connection is awaited.
Common situations: A ModuleRunner constructed with a transport whose connect is async, and the runner immediately imports a module that triggers an invoke. Custom integration code that calls invoke at startup. Race between environment init and the transport's WebSocket open.
Related errors
- transport must implement send and connect when invoke is not
- send was called before connect
- HMR is not supported by this runner transport, but `hmr` opt
- [module runner] Failed to load "${url}"${importer ? ` import
- [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/b2c0391291269a7f.json.
Report an issue: GitHub.