Skip to main content
If you’ve seen an error like Stream ended: credit lease expired or session_timeout in your console, you’re hitting the per-stream duration limit. This is expected — each stream has a maximum lifetime, but your application can start a new one immediately to keep going.

Two separate time limits

There are two clocks running independently during an interactive session: The per-stream limit is the one most people hit first. It applies to every stream individually, regardless of how much total usage your account allows.
Your account’s total usage quota controls how many hours you can stream overall. The per-stream limit is separate — each individual stream maxes out at 150 seconds before you need to start the next one.

What the error looks like

When a stream hits its duration limit, the server sends a stream error with reason session_timeout. In your browser console, you’ll see something like:
This fires through your onStreamError (JavaScript) or on_stream_error (Python) callback. The stream is done, but the fix is straightforward: start a new one.

Automatically restarting streams

The cleanest way to handle this is to catch the session_timeout error and reconnect. After a lease expiry, the server closes the underlying connection, so you’ll need to call connect() again before starting the next stream. Here’s the pattern for both SDKs:
There will be a brief visual gap (~1-2 seconds) while the new connection spins up. If you want to make this feel less jarring, show a loading state or freeze the last frame during the transition.

React example

If you’re building with React, you can wrap the reconnect logic in a hook:

Zero-downtime streaming with dual-session rollover

The reconnect patterns above work well, but there’s a visible gap while the new connection spins up. If you need truly seamless, uninterrupted video — for a live demo, a kiosk, or a production experience — you can eliminate the gap entirely by running two client instances and rolling over between them. The idea is simple: you know each stream lasts 150 seconds, so around the 130-second mark, you start warming up a second connection in the background. When it’s ready, you swap the video source and let the first connection wind down. The user never sees an interruption.
A few things to note about this pattern:
  • It uses two concurrent session slots. Make sure your account’s concurrent session limit can accommodate this.
  • The prompt stays the same across rollovers, so the visual output picks up naturally. If your application changes prompts via interact(), make sure both clients stay in sync.
  • The 20-second lead time is generous — connection setup usually takes 2-5 seconds, but the extra buffer accounts for cold starts and network variability. Tune this for your environment.
This pattern works best for always-on experiences like kiosks, installations, or live event displays where any visual interruption is unacceptable. For most interactive applications, the simpler single-session reconnect pattern above is plenty.

Things to keep in mind

  • Always wire up onStreamError / on_stream_error. Without it, your video feed goes dead when the stream expires and the user has no idea what happened.
  • Hold onto the current prompt. When you start the next stream, pass the same prompt so the visual picks up where it left off.
  • Add a short delay before reconnecting (200–500ms is plenty) in the single-session pattern. This avoids slamming the server if something goes wrong and the error fires in a tight loop.
  • Show the user something during the gap if you’re using the single-session pattern. A brief spinner or “Restarting stream…” message goes a long way compared to a frozen or black frame.

Connection vs. Stream

How connections and streams relate to each other.

Session Management

Managing session lifecycle, cleanup, and concurrent limits.