# Realtime troubleshooting

> Find why a live update did not reach your client, then fix the channel, token, connection, or publish call.

Most missed updates come from one of five places: the publish call, the token, the channel name, the topic list, or the connection. Work through the sections below in order. For a known-good publisher and subscriber to compare against, see the [Quick start](/docs-markdown/realtime/quick-start).

## The client receives no messages

1. **Check that publishing ran.** Open the function run. A `step.realtime.publish()` call appears as a step in the trace. For `inngest.realtime.publish()`, check that the code path ran and that the awaited call didn't throw.
2. **Compare resolved channel names.** A publisher on `job:123` doesn't reach a subscriber on `job:124`. Compare the channel in the publisher, the token, and `useRealtime`.
3. **Compare topics.** The publisher, the token, and `useRealtime({ topics })` must all include the topic. A token grants only its listed topics.
4. **Check subscription timing.** Realtime delivers messages only to clients connected when they're published. Wait for `connectionStatus` to be `open` before you start the work that publishes the first update. A tab that was hidden also misses messages published while it was paused.
5. **Check the hook's retained messages.** `messages.byTopic.status` holds only the latest `status` message. `messages.all` keeps up to `historyLimit` messages, 100 by default. A `bufferInterval` delays renders while it batches messages.

See [Channels and topics](/docs-markdown/realtime/channels-and-topics) for definitions and [React hooks](/docs-markdown/realtime/guides/react-hooks) for hook state.

## Publishing fails with a schema error

Compare the payload with the topic's required fields and types, and import the same channel definition in the publisher and subscriber. A runtime validator such as Zod rejects mismatched data when you publish. `staticSchema<T>()` checks types only at compile time, so a mismatch can reach the subscriber, which logs a validation error. If only the subscriber reports errors, check whether the publisher and subscriber deployed different schema versions.

## The client cannot connect or gets an authorization error

- **Mint on the server.** Call `getClientSubscriptionToken()` on your server and send the `{ key, apiBaseUrl }` object to the browser. The signing key must stay on the server.
- **Match the scope.** The token's channel and topics must cover what `useRealtime` requests. Pass `channel` and `topics` to the hook too; the client token doesn't contain them.
- **Match the environment.** Compare `apiBaseUrl` with the environment where the publisher runs. Locally, set `INNGEST_DEV=1` on the server that mints tokens.
- **Refresh tokens.** Pass a token factory so the hook gets a fresh token when it reconnects.

Never mint a token for an unchecked channel ID from the browser. See [Subscription tokens](/docs-markdown/realtime/guides/subscription-tokens).

## The connection closes or keeps reconnecting

Inspect `connectionStatus` and `error`.

- **`paused`** comes from `enabled: false` or a hidden tab while `pauseOnHidden` is `true`. `pauseReason` says which.
- **`closed`** follows a completed, failed, or cancelled run while `autoCloseOnTerminal` is `true`.
- **`error`** comes with a matching `error` value.

The hook reconnects by default. If reconnecting fails, check the token factory, the user's current access, and `apiBaseUrl`. When the job or channel ID changes, pass the new channel or change the hook's `key` to reset state and reconnect. `reset()` clears retained messages, result, and error.

## The UI shows repeated progress or tokens

`inngest.realtime.publish()` sends immediately and isn't memoized, so it publishes again when the surrounding work retries. A call outside any step can also repeat when the function resumes after a wait. To fix repeats:

- Use `step.realtime.publish(id, topic, data)` for state changes and final results, with a stable, unique step ID.
- Move immediate publishes inside `step.run()`.
- For a token stream, let the UI tolerate repeats, for example by resetting the streamed text when the step restarts.

## A server-side subscriber receives nothing

Check the `channel` and `topics` passed to `subscribe({ app: inngest, ... })` or `inngest.realtime.subscribe()`. Read the returned stream or pass `onMessage`; creating a subscription alone doesn't process messages. Pass `onError` to see connection errors, and subscribe before the work publishes. See [Server-side subscriptions](/docs-markdown/realtime/guides/server-side-subscriptions).

## Capture useful facts

When you ask for help, include the resolved channel name, topic, function run ID, publish method, `connectionStatus`, `error`, the token route's response status, and whether the tab was hidden. Don't include token keys or signing keys.

## Next steps

- [Overview](/docs-markdown/realtime/overview) explains how messages reach clients.
- [Guides](/docs-markdown/realtime/guides) cover tokens, React, AI streaming, and server-side subscriptions.
- [Reference](/docs-markdown/realtime/reference) lists every Realtime API.
- [Limits](/docs-markdown/realtime/limits) covers connection and message allowances.