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

  1. Await transport.connect() before sending — ensure startup code awaits the connection promise.
  2. If using the ModuleRunner, let it manage connect ordering; do not call send on the raw transport yourself.
  3. 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

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


AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03). Data as JSON: /data/errors/b0bb8f464c398d31.json. Report an issue: GitHub.