vitejs/vite · error · Error

Cannot send non-custom events from the server to the client.

Error message

Cannot send non-custom events from the server to the client.

What it means

Thrown by the server module runner transport's send() (server -> client direction) when the payload type is not 'custom'. The runner transport only forwards custom events from the dev server to the SSR module runner; built-in HMR messages take a different path.

Source

Thrown at packages/vite/src/node/ssr/runtime/serverModuleRunner.ts:126

        undefined,
        hmrClient,
      )
      onMessage({ type: 'connected' })
      handler = onMessage
    },
    disconnect() {
      if (handler) {
        options.channel.api!.outsideEmitter.off('send', handler)
      }
      options.channel.api!.innerEmitter.emit(
        'vite:client:disconnect',
        undefined,
        hmrClient,
      )
    },
    send(payload) {
      if (payload.type !== 'custom') {
        throw new Error(
          'Cannot send non-custom events from the server to the client.',
        )
      }
      options.channel.api!.innerEmitter.emit(
        payload.event,
        payload.data,
        hmrClient,
      )
    },
  }
}

/**
 * Create an instance of the Vite SSR runtime that support HMR.
 * @experimental
 */
export function createServerModuleRunner(
  environment: DevEnvironment,

View on GitHub (pinned to b4d66fee14)

Solutions

  1. Only send custom events via this transport: send({ type: 'custom', event, data }).
  2. Use environment.hot.send(...) or the regular HMR channel for built-in payload types.
  3. If you only need RPC, prefer transport.invoke(...) which is wrapped as a custom vite:invoke event.

Example fix

// before
transport.send({ type: 'update', updates }) // throws
// after
transport.send({ type: 'custom', event: 'my-event', data: updates })
Defensive patterns

Strategy: validation

Validate before calling

// only send custom payloads via the runner transport
transport.send({ type: 'custom', event, data })

Type guard

function isCustomPayload(p: HotPayload): boolean {
  return p.type === 'custom'
}

Prevention

When it happens

Trigger: Server-side code (plugin, framework SSR integration) calling transport.send(payload) with a payload whose type is not 'custom'. The guard is at serverModuleRunner.ts:125.

Common situations: A plugin trying to broadcast an 'update'/'connected' payload to the SSR runner through the module-runner transport instead of the regular HMR channel. Misuse of the transport's send for built-in HMR orchestration.

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/66f94e0bf3981c3f. Report an issue: GitHub.