> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors and limits

> Look up why a Rime speech-to-text stream failed and whether to retry it. The page also lists the limits, fixes for common problems, and a checklist before production.

A stream that ends without a final transcript has failed. The last partial transcript of a failed stream is incomplete, even when it looks like a full sentence.

## Error categories

Branch on the category, not on the message text, because the message text can change without notice.

| Category | What happened | [gRPC status](https://grpc.io/docs/guides/status-codes/) | Retry? |
| - | - | - | - |
| Invalid input | The request broke a rule. Causes are a missing or unsupported language, a missing output contract, or an invalid mode or context term. Other causes are messages out of order, and an empty or odd-length audio chunk. | `INVALID_ARGUMENT` | No. Fix the request. |
| Resource limit | The utterance, a message, or the model's context went over a [limit](#limits). Or your API key went over its rate limit, or the service had no capacity for the stream. | `RESOURCE_EXHAUSTED` | For a rate limit or no capacity, a few times with backoff. If the request went over another limit, no. Fix the request. See [Decide whether to retry](#decide-whether-to-retry). |
| Timeout | A deadline passed: 5 seconds to start, 15 seconds without audio, or 120 seconds in total. | `DEADLINE_EXCEEDED` | Yes, after you fix the cause. Use a new stream. |
| Unavailable | The service was not ready, or it was shutting down. Or recognition ended without a final transcript. | `UNAVAILABLE` | Yes. Use a new stream after a short backoff. |
| Cancelled | The stream was cancelled. | `CANCELLED` | No |
| Internal | An unexpected failure. | `INTERNAL` | Once. If it fails again, contact support with the request ID. |
| Audio format (SDK only) | The SDK rejected your audio. The input format is not supported, and the SDK finds this before it sends anything. Or the source ended partway through a sample, and the SDK finds this when the source ends. | None | No. Fix the audio source. |
| Authentication | The API key was missing, empty, or not valid. The SDK finds a missing or empty `RIME_API_KEY` when you create the client. Rime refuses a missing or invalid key before the stream starts. | `UNAUTHENTICATED` | No. Set a valid key. |

Each client uses its own name for each category:

| Category | Python and Node.js | WebSocket `kind` |
| - | - | - |
| Invalid input | `RimeInputError` | `invalid_input` |
| Resource limit | `RimeResourceLimitError` | `resource_exhausted` |
| Timeout | `RimeTimeoutError` | `timeout` |
| Unavailable | `RimeUnavailableError` | `unavailable` |
| Cancelled | `RimeCancelledError` | `cancelled` |
| Internal | `RimeStreamError`, which also covers protocol failures that the SDK finds on its own side of the stream | `internal` |
| Audio format | `RimeAudioFormatError` | None |
| Authentication | `RimeAuthenticationError` | HTTP `401` on the upgrade |

> **Go and Rust error names**
>
> | Category | Go (`errors.Is`) | Rust (`ErrorKind`) |
> | - | - | - |
> | Invalid input | `rime.ErrInput` | `ErrorKind::Input` |
> | Resource limit | `rime.ErrResourceLimit` | `ErrorKind::ResourceLimit` |
> | Timeout | `rime.ErrTimeout` | `ErrorKind::Timeout` |
> | Unavailable | `rime.ErrUnavailable` | `ErrorKind::Unavailable` |
> | Cancelled | `rime.ErrCancelled` | `ErrorKind::Cancelled` |
> | Internal, stream, or protocol failure | `rime.ErrStream` | `ErrorKind::Stream` |
> | Audio format | `rime.ErrAudioFormat` | `ErrorKind::AudioFormat` |
> | Authentication | `rime.ErrAuthentication` | `ErrorKind::Authentication` |
>
> The [Go](https://github.com/rimelabs/rime-sdk/blob/main/go/README.md#help-and-licenses) and [Rust](https://github.com/rimelabs/rime-sdk/blob/main/rust/README.md#help-and-licenses) READMEs show how to check the category and read the request ID.

> **WebSocket upgrade refusals**
>
> Before a WebSocket stream starts, the server can refuse the upgrade. It sends an HTTP response, and no `error` event follows.
>
> | HTTP status | Cause | What to do |
> | - | - | - |
> | `400` | The request is not a WebSocket upgrade. | Connect with a WebSocket client. |
> | `401` | The `Authorization` header is missing, or Rime does not accept the key. The body is empty. | Check the key on the [API Tokens page](https://app.rime.ai/tokens). |
> | `404` | The path has no route, for example `/stt`. | Connect to `wss://stt.api.rime.ai/ws`. |
> | `503` | The service is not ready, is shutting down, or has no WebSocket capacity. | Retry after a backoff. |

## Decide whether to retry

Retry only when you still have the audio, and always retry on a new stream. The SDK does not retry or send audio again on its own. Keep the transcript of each attempt separate, and log the request ID of each attempt.

A resource-limit error does not always say whether the request went over a limit or the service had no capacity for it. Handle a resource-limit error in this order:

1. Compare what you sent with the [limits](#limits) that you can measure. These are the size of each message and of the utterance, the context terms, and the 120-second limit. If the request went over a limit, fix the request rather than retry it. For example, a direct client that sent an audio message over 64 KiB must split that message, not shorten the utterance.
2. If the request is within these limits, the cause is unknown. Retry a small number of times, such as two or three. Use backoff and a new stream for each attempt.
3. If the error continues, the likely cause is the model's context. This cause comes back on every retry of the same audio. [Split the audio across streams](#a-long-turn-or-recording-fails), or shorten the context terms.

The time of the error helps you find the cause:

| Transport | When the error arrives | Likely cause |
| - | - | - |
| WebSocket | A `resource_exhausted` error before `started`, for a `start` message well under 96 KiB | Service capacity. Before `started`, the service checks only its own capacity and the 96 KiB size of the `start` message. |
| WebSocket or gRPC streaming | Late in a long utterance | The [model's context](#limits) |
| gRPC `Transcribe` or the SDK's `transcribe` call | Any time | No signal. The call can fail a size or context check before it sends any response. |

A timeout comes back on retry if you do not fix its cause. To prevent one, send `start` or `config` as soon as you connect, send audio through pauses, and end each utterance well before the 120-second limit.

## Limits

| Limit | Value | Applies to | When you exceed it |
| - | - | - | - |
| Time to send `start` or `config` after connecting | 5 seconds | Direct WebSocket and gRPC clients. The SDK sends it immediately. | `timeout` `DEADLINE_EXCEEDED` |
| Time without audio | 15 seconds | Every stream | `timeout` `DEADLINE_EXCEEDED` |
| Total time per stream, from connecting to the final transcript | 120 seconds | Every stream | `timeout` `DEADLINE_EXCEEDED` |
| Audio per utterance, decoded | 16 MiB | Every stream | `resource_exhausted` `RESOURCE_EXHAUSTED` |
| Audio per message, decoded | 64 KiB, non-empty, with an even byte count | Direct streaming clients. The SDK splits the audio for you. | `resource_exhausted` over 64 KiB. `invalid_input` if empty or odd. |
| WebSocket message, encoded | 96 KiB | WebSocket | `resource_exhausted` |
| Context terms, joined with `, ` | 1,024 UTF-8 bytes | Every request | `invalid_input` `INVALID_ARGUMENT` |
| Transcript per utterance | 1 MiB | Every stream | `resource_exhausted` `RESOURCE_EXHAUSTED` |
| Transcript per utterance, in the SDK | 64 KiB of UTF-8 text | SDK streams. The SDK checks this limit before the service checks its 1 MiB limit. | Resource-limit error |
| Model context | Depends on the audio, the context terms, and the transcript so far. It can run out before 120 seconds. | Every request | `resource_exhausted` `RESOURCE_EXHAUSTED` |
| WAV file for `Transcribe` | 16 MiB | gRPC `Transcribe`, and the SDK's `transcribe` call, which checks the size before it sends the file | `RESOURCE_EXHAUSTED`, or a resource-limit error in the SDK |
| Transcription time for `Transcribe` | 120 seconds | gRPC `Transcribe` and the SDK's `transcribe` call | `DEADLINE_EXCEEDED`, or a timeout error in the SDK |

Two of these limits deserve more explanation:

**15 seconds without audio.** These events change the timer:

| Event | Effect on the 15-second timer |
| - | - |
| You send an audio message, silence included | The timer restarts. |
| The service sends an update | The timer restarts. |
| The service applies backpressure | The timer pauses. |
| You end the audio | The timer stops. After that, only the 120-second limit applies. |
| A WebSocket ping or a gRPC keepalive | No effect. These do not count as audio. |

**Model context.** The model reads the utterance's audio, your context terms, and the transcript so far as one input. That input has a fixed maximum size, and the output contract decides whether the input grows with the utterance:

* **Revised hypotheses:** the SDK uses this contract. The model reads all of the audio again for each update, so the context grows with the utterance. A long utterance can run out of context before the 120-second limit. The service never returns a shorter transcript in place of the error.
* **Append fragments:** the model recognizes each five-second window on its own, so the context does not grow with the utterance.

In Rime's tests with English speech, revised-hypotheses streams ran out of context after about 90 to 110 seconds of speech. gRPC `Transcribe` lasted about 10 seconds longer. Fast speech, a long list of context terms, and silence in the stream fill the context sooner. No speaking rate in words per minute guarantees a length, so [split long turns and recordings](#a-long-turn-or-recording-fails) well before this point.

## Common problems

### The SDK fails as soon as you create the client

If `RIME_API_KEY` is missing or empty when you create the client, Python and Node.js raise `RimeAuthenticationError` at that point, before any request. The SDK does not read `.env` files, so set the variable in the environment that runs your code, and keep its value out of logs.

### Rime refuses the API key

If Rime does not accept the key, it refuses the stream before the stream starts. The SDK raises its authentication error, a WebSocket upgrade gets HTTP `401`, and gRPC returns `UNAUTHENTICATED`. Check that the key is current, or [create a new key](/docs/api-authentication).

### Words repeat in the transcript

Your code appends partial transcripts. [Replace each one](/docs/stt/live-audio#show-partials-and-act-on-the-final) instead.

### Partials arrive, but no final transcript

The stream still waits for the end of the audio. End the audio source:

| Client | End the audio |
| - | - |
| SDK | Return from the generator, put your end marker on the queue, or end the `PassThrough`. A chunk of zero bytes does not end the source. |
| WebSocket | Send `end`. |
| gRPC | Half-close the request stream. |

### The stream times out during a pause

The service got no audio for 15 seconds. Send captured audio through every pause. If a step before your backend drops silent frames, fill the gap with frames of zeros.

### The transcript is poor or empty

Check these items in order:

1. **Language.** Speech in a language other than the one that you selected returns a poor transcript, not an error.
2. **Audio format.** The SDK reads every byte as PCM16 samples. Remove a WAV header, and decode MP3 or mu-law audio, before you send it. Set `input_format` to the rate and channel count of your capture.
3. **Silence.** An empty final transcript is a successful result for audio with no recognizable speech.
4. **Names and jargon.** Add [context terms](/docs/stt/recognition#context-terms) for words that the model gets wrong.

### A long turn or recording fails

A resource-limit error late in a long utterance usually means that the utterance ran out of [model context](#limits). A long call made of short turns does not have this problem, because each turn has its own stream. A retry of the same audio fails again, and gRPC `Transcribe` holds only a little more audio, so split long audio across streams:

* **Live audio:** End the utterance at the first pause after about a minute of speech, and start the next stream immediately. [Split long turns at pauses](/docs/stt/live-audio#split-long-turns-at-pauses) shows how to do this with the SDK, and how to join the final transcripts.
* **Recordings:** Stream the file through the WebSocket or gRPC API with append fragments. Send the audio faster than real time, in parts of at most 16 MiB, as [Transcribe a long recording](/api-reference/stt/websockets#transcribe-a-long-recording) describes.

## Report a failure

Get the request ID from the stream, or from the error that it raised:

| Client | Request ID |
| - | - |
| Python SDK | `transcript.request_id`, or `error.request_id` |
| Node.js SDK | `transcript.requestId`, or `error.requestId` |
| Go SDK | `transcript.RequestID()` |
| Rust SDK | `transcript.request_id()` |
| WebSocket | `started.requestId`, every `error.requestId`, and the `x-request-id` header of the upgrade response |
| gRPC | `x-request-id` in the response headers, and in the trailers of a failed call |

Email [support@rime.ai](mailto:support@rime.ai) with:

* the request ID and the UTC time of the failure
* the error category and its message
* the SDK and version, or the transport that you called directly
* the audio format, and how your code ended the audio

Do not put your API key in the report.

## Before production

* Your API key is only on your backend.
* You pin your SDK version, because the packages are in alpha.
* Your code acts only on final transcripts.
* You log the request ID of every stream.
* Each stream opens when speech starts, and your backend sends silence during pauses.
* You split long turns at pauses, and long recordings use append fragments.
* Each error category has a handler, and each retry uses a new stream.
* You set `language` from what you know about the speaker.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.