vitejs/vite · error · SendBeforeConnectError
send was called before connect
Error message
send was called before connect
What it means
Vite's normalized module-runner transport tracks a connection state. After wrapping, send() checks isConnected: if not connected and no connect() is in flight (no connectingPromise), it throws SendBeforeConnectError because messages cannot be delivered on an unestablished channel. This guards against losing messages that are sent before the transport's connect() resolves.
Source
Thrown at packages/vite/src/shared/moduleRunnerTransport.ts:240
? {
async disconnect() {
if (!isConnected) return
if (connectingPromise) {
await connectingPromise
}
isConnected = false
await invokeableTransport.disconnect!()
},
}
: {}),
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 {View on GitHub (pinned to 89620f09af)
Solutions
- Await transport.connect() before sending — ensure startup code awaits the connection promise.
- If using the ModuleRunner, let it manage connect ordering; do not call send on the raw transport yourself.
- For custom transports, set up the channel synchronously (e.g. readyState OPEN) or await its open event before the runner sends.
Example fix
// before — sending before the connection is established
const transport = normalizeModuleRunnerTransport(myTransport)
await transport.send({ type: 'custom', event: 'ping', data: {} }) // throws
// after — connect first
const transport = normalizeModuleRunnerTransport(myTransport)
await transport.connect()
await transport.send({ type: 'custom', event: 'ping', data: {} }) Defensive patterns
Strategy: validation
Validate before calling
// Ensure connect is awaited before any send await transport.connect() // now safe to send await transport.send(data)
Try / catch
import { SendBeforeConnectError } from 'vite/module-runner'
try {
await transport.send(data)
} catch (e) {
if (e instanceof SendBeforeConnectError) {
await transport.connect()
await transport.send(data)
} else {
throw e
}
} Prevention
- Always await transport.connect() before sending.
- Let the ModuleRunner manage connect/send ordering; avoid manual sends.
- Where possible, make the underlying channel open synchronously.
When it happens
Trigger: Calling transport.send(data) on a normalized module-runner transport before connect() has been awaited (and no connect is currently pending). Happens when a module runner or consumer fires a message synchronously at startup before awaiting the transport connection.
Common situations: A custom module-runner transport where the runner starts importing modules (triggering sends) before the underlying channel (WebSocket, worker) is open. Race conditions in startup ordering where the HMR/RPC send is dispatched during construction. Test harnesses that create a runner and immediately send without connecting.
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
- transport must implement send and connect when invoke is not
- [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/b0bb8f464c398d31.json.
Report an issue: GitHub.