Error Handling

View as Markdown

The SDK provides a hierarchy of custom exceptions for robust error handling. Catch WSSError to handle any SDK-raised error, or catch specific subclasses when you need call- or buffer-level recovery.

Exception Hierarchy

WSSError (base)
|- WSSConnectionError # Connection-related errors
|- WSSAuthenticationError # Authentication failures
|- WSSMediaError # Media connection errors
|- BufferFullError # Outgoing audio buffer is full
|- BufferClosedError # Outgoing audio buffer has already closed
`- WSSCallError # Call-related errors
|- WSSCallClosedError # Operation on a closed call
|- WSSCallCommandError # Server rejected the command
`- WSSCallCommandTimeoutError # Command response timeout

Exception Details

ExceptionWhen It’s Raised
WSSErrorBase exception for all SDK errors
WSSConnectionErrorThe control or media connection fails
WSSAuthenticationErrorAPI key or mTLS authentication fails
WSSMediaErrorThe media connection cannot be established or drops unexpectedly
BufferFullErrorThe outgoing audio buffer reaches capacity
BufferClosedErrorYour code tries to write audio after the buffer has already closed
WSSCallErrorBase class for call-specific errors
WSSCallClosedErrorYou attempted an operation on a call that has already ended
WSSCallCommandErrorThe server rejected a command, exposing error_code and error_message
WSSCallCommandTimeoutErrorThe client timed out waiting for a command response

Example

1from telcoflow_sdk.exceptions import (
2 BufferClosedError,
3 BufferFullError,
4 WSSCallClosedError,
5 WSSCallCommandError,
6 WSSError,
7)
8import telcoflow_sdk.events as events
9
10@client.on(events.INCOMING_CALL)
11async def handle_call(call: ActiveCall):
12 try:
13 await call.answer()
14
15 async for chunk in call.audio_stream():
16 response = await ai_model.generate(chunk)
17 try:
18 await call.send_audio(response)
19 except BufferFullError:
20 await call.clear_send_audio_buffer()
21 except BufferClosedError:
22 break
23
24 except WSSCallClosedError:
25 print("Call was already closed")
26 except WSSCallCommandError as exc:
27 print(f"Command failed: {exc.error_code} {exc.error_message}")
28 except WSSError as exc:
29 print(f"SDK error: {exc}")

Handling CALL_UNANSWERED for connect()

When you call connect(), the callee may not answer within the ring window. In that case the SDK raises WSSCallCommandError with error_code == "CALL_UNANSWERED" and the call remains active, so you can retry, try a different branch in your flow, or end the call.

1from telcoflow_sdk import WSSCallCommandError
2import telcoflow_sdk.events as events
3
4@client.on(events.INCOMING_CALL)
5async def assistant_flow(call: ActiveCall):
6 await call.answer()
7 try:
8 await call.connect(ring_time_seconds=30)
9 except WSSCallCommandError as exc:
10 if exc.error_code == "CALL_UNANSWERED":
11 await call.disconnect()
12 else:
13 raise

Best Practices

  • Catch specific exceptions first - Put WSSCallClosedError or buffer exceptions before WSSError
  • Handle buffer pressure in the audio loop - Recover from BufferFullError where you are calling send_audio()
  • Expect race conditions near call shutdown - BufferClosedError and WSSCallClosedError can happen when a caller hangs up mid-response
  • Use error_code for command-specific recovery - CALL_UNANSWERED is the clearest example for connect() flows
  • Choose the right shutdown method - Use close() carefully because its behavior depends on call state: after connect() the agent leaves, but after answer() without connect() the caller is disconnected too. Use disconnect() when you explicitly want the whole call to end

Next Steps