> For the complete documentation index, see [llms.txt](https://docs.ojin.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ojin.ai/guides/troubleshooting.md).

# Troubleshooting

Common symptoms when building real-time avatars, with their cause and fix.

{% hint style="info" %}
**🟢 SDK-handled**: the [Python SDK](/models/build-with-python-sdk.md) and [Pipecat](/models/introduction/integrations.md) take care of this for you; it surfaces only on the raw WebSocket API (or if you turn a feature off). The simplest fix is usually "use the SDK."

**🟠 Your setup**: you own this regardless of integration: deployment, network, credentials, capacity, or raw-API tuning.
{% endhint %}

## Jump to a symptom

* [Mouth barely moves, idle animation only, or the avatar jumps between utterances](#mouth-barely-moves-idle-animation-only-or-the-avatar-jumps-between-utterances)
* [Lip-sync drifts out of sync with the audio](#lip-sync-drifts-out-of-sync-with-the-audio)
* [Choppy or crackling playback](#choppy-or-crackling-playback)
* [No video frames at all](#no-video-frames-at-all)
* [Growing latency over time](#growing-latency-over-time)
* [Frame lag during speech on the raw API](#frame-lag-during-speech-on-the-raw-api)
* [No backend servers available](#no-backend-servers-available)
* [Connection or authentication fails](#connection-or-authentication-fails)
* [Interruptions look abrupt or the avatar keeps speaking after a barge-in](#interruptions-look-abrupt-or-the-avatar-keeps-speaking-after-a-barge-in)
* [Direct WebRTC: the avatar can't join the room](#direct-webrtc-the-avatar-cant-join-the-room)
* [Direct WebRTC: the session times out before it's ready](#direct-webrtc-the-session-times-out-before-its-ready)
* [Direct WebRTC: the avatar drops out mid-session](#direct-webrtc-the-avatar-drops-out-mid-session)
* [Direct WebRTC: WEBRTC\_NOT\_SUPPORTED](#direct-webrtc-webrtc_not_supported)
* [Direct WebRTC: my bot hears the avatar](#direct-webrtc-my-bot-hears-the-avatar)

## Mouth barely moves, idle animation only, or the avatar jumps between utterances

🟢 **SDK-handled**

**Cause:** the server is running short of audio. It needs about **1.3 seconds** before it can start generating, and it needs your input to stay ahead of its 25 fps output after that. Forwarding tiny TTS fragments one at a time, or leading with only a second, leaves it waiting, so it can't sustain speech and emits idle frames in between.

Running short also shows up as a **jump**. If nothing arrives for about **280 ms** while the server is short, it treats the turn as finished, plays non-speech frames, and starts a new turn when your audio resumes. The pose can shift across that boundary.

A slow TTS provider triggers this with no network problem at all: 1 s of audio, then the next second 800 ms later, crosses the 280 ms threshold every time. The server does smooth the easier cases on its own, covering brief stalls with recent frames and absorbing ordinary frame jitter, but it cannot cover audio that is not there.

**Fix:** lead each turn with 2-3 s of audio, then stream in real time with the largest chunks you can (\~400 ms). The lead holds the server above its 1.3 s requirement, so the 280 ms countdown never opens. See [Optimizing Performance → Feed audio](/guides/optimizing-performance.md#feed-audio-for-stable-low-latency-lip-sync). `OjinSTVClient` already does this for you, so the simplest fix is to drive the model through the [Python SDK](/models/build-with-python-sdk.md) or [Pipecat](/models/introduction/integrations.md).

## Lip-sync drifts out of sync with the audio

🟢 **SDK-handled**

**Cause:** on the SDK path, you're adding your own A/V re-sync on top of an already-synced stream. On the raw API, you're playing your own copy of the TTS audio on its own clock instead of the audio that arrives with each frame, or frames were reordered during async decode.

**Fix:** with the SDK or Pipecat, `output_stream()` is already aligned at 25 fps. **don't re-sync it yourself**. Present each frame as it arrives, or pass each frame's `pts` to your media transport (e.g. WebRTC) and let it handle sync. If you build your own loop on the raw API, play the audio payload that arrived with each frame and render that frame's image at the same time. The server aligns them for you, so no audio master clock is needed. Keep decode order stable. Note that per-frame audio also reproduces any silence the server inserted while it was running short, so fix the audio lead first. See [Audio and Video Synchronization](/models/introduction/api.md#audio-and-video-synchronization) in the API Reference.

## Choppy or crackling playback

🟢 **SDK-handled**

**Cause:** audio isn't playing at a steady rate, or the event loop is blocked.

**Fix:** the SDK emits exactly one audio frame every 40 ms tick — real audio or silence — so its output never starves on its own. Keep your frame handlers light: heavy synchronous work stalls the tick that paces them. On the raw API, play audio gaplessly at a steady rate and never stop it just because a video frame is late. See [Optimizing Performance → Play back at realtime](/guides/optimizing-performance.md#play-back-at-realtime).

## No video frames at all

🟢 **SDK-handled**

**Cause:** you haven't received `SESSION_READY` / `SessionReady` yet, or (SDK) you're using a `PassthroughDecoder`, which suppresses video frames entirely.

**Fix:** wait for the session-ready signal before sending audio. With the SDK, keep the default decoder: it populates `frame.rgb` and also carries the raw JPEG in `frame.source_bytes`, so you can relay without re-encoding. A `PassthroughDecoder` makes the client emit no video frames at all, so do not use it. See [Optimizing Performance → Relay frames](/guides/optimizing-performance.md#relay-frames-without-re-encoding).

## Growing latency over time

🟢 **SDK-handled** · 🟠 **Your setup** (deployment)

**Cause:** your buffer is too large, or frames backed up after a network stall. The model delivers at realtime 25 fps, so the buffer shouldn't grow on its own. Keep only a small jitter buffer.

**Fix:** the SDK keeps the buffer small and trims backed-up idle frames for you (`idle_buffer_target_frames`, default 6), so don't filter `output_stream()` by `frame_type` yourself — it fights the client's own catch-up. On the raw API, keep the buffer to a few frames and, if frames back up on a poor connection, trim idle frames (`frame_type == 0`); never drop speech, start-of-speech, or fade-out frames. Either way, run the client on a backend in **US East** over a stable connection. See [Optimizing Performance → Where to run](/guides/optimizing-performance.md#where-to-run).

## Frame lag during speech on the raw API

🟠 **Your setup** (raw-API tuning)

**Cause:** your playback buffer is larger than it needs to be, so every frame waits behind the ones ahead of it.

**Fix:** shrink the jitter buffer to the smallest size your network tolerates. Output smoothing itself is not caller-tunable: there is no parameter on the wire protocol that changes it. If the lag persists at a small buffer, contact support with a session id rather than trying to tune it from the client.

## No backend servers available

🟠 **Your setup**

**Cause:** inference capacity is momentarily exhausted.

**Fix:** retry shortly with backoff. On the SDK this surfaces as an `ERROR` event with code `NO_BACKEND_SERVER_AVAILABLE`. On the raw API it arrives either as an `ErrorResponse` with `BACKEND_UNAVAILABLE`, or as a bare plain-text frame with no code at all (`"No backend servers available. Please try again later."`) — handle non-JSON text frames too.

## Connection or authentication fails

🟠 **Your setup**

**Fix:**

* ✓ Verify your API key and `config_id`, and that the config exists in your [dashboard](https://ojin.ai/dashboard)
* ✓ The `Authorization` header must use the **raw API key** (no `Bearer` prefix)
* ✓ Ensure your network allows WebSocket connections on port 443

## Interruptions look abrupt or the avatar keeps speaking after a barge-in

🟢 **SDK-handled**

**Cause:** frames already sent or buffered keep playing after you cancel.

**Fix:** the SDK's `interrupt()` fades audio and cancels server-side for you. On the raw API, pick an interruption strategy for your latency/smoothness trade-off: clear the buffer for an instant cut, or keep playing video while stopping audio for a smoother look. See [Interruption Handling](/models/introduction/api.md#interruption-handling) in the API Reference.

## Direct WebRTC: the avatar can't join the room

🟠 **Your setup** (credentials)

**Cause:** the room rejected the avatar. You get a fatal `ERROR` whose `code` names the cause: `WEBRTC_AUTH_FAILED` (the token doesn't match the room), `WEBRTC_NETWORK_FAILED` (Ojin couldn't reach the room), or `WEBRTC_INVALID_SETTINGS` (the room URL or provider was unusable).

**Fix:**

* ✓ **LiveKit:** `room_url` is your server URL (`wss://…`), and the token allows room join and publish for the right room, with the identity `ojin-avatar`
* ✓ **Daily:** `room_url` is the room URL and `token` is a meeting token for **that** room
* ✓ The token hasn't expired
* ✓ `WEBRTC_NETWORK_FAILED`: check that your LiveKit or Daily service is reachable and retry

See [Direct WebRTC → Prerequisites](/models/build-with-python-sdk/python-sdk-webrtc.md#prerequisites).

## Direct WebRTC: the session times out before it's ready

🟠 **Your setup**

**Cause:** the session wasn't ready within `webrtc_join_timeout_s` (default 10 s). The timeout covers both the room join and the model's cold start, so a cold start alone can exceed it. You get a fatal `WEBRTC_JOIN_TIMEOUT`.

**Fix:** raise `webrtc_join_timeout_s` to about **30 s** in production.

## Direct WebRTC: the avatar drops out mid-session

🟠 **Your setup**

**Cause:** the avatar lost the room after joining it — you get a fatal `WEBRTC_ROOM_LOST`. The client never rejoins on its own.

**Fix:** start a new session. If it recurs, check the room's lifetime and token expiry against your session length.

## Direct WebRTC: WEBRTC\_NOT\_SUPPORTED

🟠 **Your setup**

**Cause:** the server returned no room result, so this session can't be published into a room. The client doesn't fall back to the WebSocket; it emits a fatal `ERROR` and closes the session.

**Fix:** leave out `webrtc=` to use the WebSocket mode, or [contact support](/getting-started/support.md) with your `config_id`.

## Direct WebRTC: my bot hears the avatar

🟠 **Your setup**

**Cause:** your own voice agent is in the same room and subscribes to the avatar's microphone, so it transcribes the avatar's voice as user speech and replies to itself.

**Fix:** unsubscribe your bot from the avatar's audio. Identify the avatar with `is_avatar_participant()` (Daily) or `is_avatar_identity()` (LiveKit) from `ojin`. See [Direct WebRTC (LiveKit & Daily)](/models/build-with-python-sdk/python-sdk-webrtc.md).

***

Still stuck? [Contact support](/getting-started/support.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ojin.ai/guides/troubleshooting.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
