Skip to main content

Error frame

When something goes wrong after the socket is accepted, the server sends a JSON error frame:
{
  "type": "error",
  "code": "session_conflict",
  "message": "another active session exists for this user",
  "fatal": true
}
type
string
Always "error".
code
string
A stable, machine-readable code string. Branch on this — see the table below.
message
string
A short, human-readable, content-free description. Safe to log. Never contains audio or transcript text.
fatal
boolean
When true, the session cannot continue: the server closes the socket immediately after this frame. When false, the session continues and you may keep streaming.
Always branch on the boolean fatal field, not on the code alone. Some codes can be delivered either fatally or non-fatally depending on context.

Error codes

CodeTypically fatalMeaningWhat to do
auth_failedyes (close 1008)The JWT was invalid, expired, identified no user, or lacked the transcribe permission.Obtain a fresh, valid token with the right permission and reconnect. Delivered as close code 1008 during the handshake — usually without a JSON frame.
config_timeoutyesNo config frame arrived within ~10 seconds of connecting.Send the config frame immediately on open.
config_invalidyesThe first message was not a valid config — malformed JSON, wrong/missing type, unknown or extra fields, or out-of-range values.Fix the config payload (send only documented fields, valid ranges) and reconnect.
session_conflictyesAnother active session already exists for this user.Close the previous socket, then reconnect. See one session per user.
overloadedyesThe service is momentarily at capacity.Retry after a short backoff (with jitter).
unavailableyesThe service is starting up or temporarily not ready.Retry shortly.
asr_failedusually noTranscription failed for a segment.If non-fatal, keep streaming — a single segment may be missing. If fatal, reconnect (resuming from processed_until).
internal_errorvariesAn unexpected server-side condition.Check fatal. If fatal, reconnect after a short backoff.
auth_failed is special: authentication is checked during the handshake, so a failure is signaled by closing the socket with code 1008 rather than by an in-session JSON error frame. Detect it in your onclose handler.

Fatal & close semantics

1

Handshake rejection (auth)

An authentication failure closes the socket with code 1008 before acceptance. There is no session_ready and typically no JSON error frame.
2

In-session fatal error

After acceptance, a fatal condition is sent as an error frame with "fatal": true, and the server then closes the socket. Handle the frame, then treat the close as expected.
3

Non-fatal error

An error frame with "fatal": false is informational — the session stays open and you can keep streaming.
4

Clean close

You end a session with a close control frame (or by closing the socket). The server finalizes any buffered audio first.
Error handling
ws.onmessage = (event) => {
  if (typeof event.data !== "string") return;
  const msg = JSON.parse(event.data);
  if (msg.type !== "error") return;

  console.warn("error:", msg.code, msg.message);

  switch (msg.code) {
    case "session_conflict":
      // A previous session is still open; close it, then retry.
      break;
    case "overloaded":
    case "unavailable":
      scheduleReconnect(backoffWithJitter());
      break;
    case "config_invalid":
    case "config_timeout":
      // Fix the config; do not blindly retry the same payload.
      break;
    default:
      if (!msg.fatal) return; // non-fatal: keep going
  }
  if (msg.fatal) ws.close();
};

ws.onclose = (e) => {
  if (e.code === 1008) {
    console.error("Authentication failed — refresh the token and reconnect");
  }
};

Backpressure handling

Backpressure is not an error — it is a flow-control signal telling you that audio is arriving faster than the session can finalize it.
{ "type": "backpressure", "level": "warn", "dropped_segments": 1 }
level
string
Severity, for example "warn".
dropped_segments
integer
How many buffered segments were dropped to keep the session live (0 on an early warning, before anything is dropped).
What to do:
1

Send closer to real time

Pace your binary frames to the audio’s own duration. The most common cause is streaming a file or backlog far ahead of real time.
2

Reduce burst size

Send smaller, more frequent frames rather than large bursts.
3

Recover dropped audio if needed

If dropped_segments > 0 and you cannot tolerate the gap, use your buffer and the reconnection primitives to resend audio after the last processed_until.
A backpressure frame may include additional advisory numeric fields; treat them as opaque. The correct response is always to slow the send rate toward real time.

status frames

Not every notification is an error. The server may send a status frame for informational session-state changes:
{ "type": "status", "state": "idle_timeout" }
For example, idle_timeout means the session received no audio for an extended period (roughly two minutes) and is being closed. Keep a session alive by sending audio, or an end_utterance, periodically.

Getting help

Contact support

If you hit an error you cannot resolve, email support@medisync.me with the error code, message, and the time of the failure. Never include audio, transcript text, or your JWT.

Next steps

Reconnection

Recover from fatal drops with no lost transcript.

Connection

Close codes and the handshake sequence.