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

  1. Implement invoke on your transport (preferred for simpler integrations — a single async RPC function).
  2. Or implement both send(data) and connect({ onMessage, onDisconnection }) so Vite can build RPC over them.
  3. 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

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


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