Skip to content

AsyncV2SocketClient.__aiter__ silently yields None for Warning messages (SDK 7.8.1) #792

Description

@pdonepud

Summary

The Listen v2 AsyncV2SocketClient.__aiter__ (deepgram/listen/v2/socket_client.py:35-46) uses construct_type(type_=V2SocketClientResponse, object_=...) to decode incoming messages. V2SocketClientResponse is a Union[ListenV2Connected, ListenV2TurnInfo, ListenV2ConfigureSuccess, ListenV2ConfigureFailure, ListenV2FatalError] — it does not include a Warning member.

For messages whose type field is not in the union (e.g. Flux's Warning with code FORCE_END_TURN_NO_ACTIVE_TURN), construct_type returns None rather than raising. The iterator's surrounding except Exception guard therefore does not fire, and the bare None is silently yielded to consumers.

Reproduction

import json
from deepgram.core.unchecked_base_model import construct_type
from deepgram.listen.v2.socket_client import V2SocketClientResponse

payload = {
    "type": "Warning",
    "request_id": "test",
    "sequence_id": 1,
    "code": "FORCE_END_TURN_NO_ACTIVE_TURN",
    "description": "no active turn",
}
result = construct_type(type_=V2SocketClientResponse, object_=payload)
print(result)  # None

Verified against deepgram-sdk==7.8.1 on Python 3.11.

Impact

Client code that pattern-matches on the SDK's typed union (e.g. isinstance(msg, ListenV2TurnInfo)) cannot see Warning messages at all — they show up as bare None yields, indistinguishable from any other unknown-type payload.

Per the Flux docs, sending ForceEndTurn before Flux has emitted StartOfTurn (e.g. a silent push-to-talk turn) results in a Warning FORCE_END_TURN_NO_ACTIVE_TURN — the documented correct handling is "no active turn, treat as empty completion." Consumers using the SDK's public typed iterator can't reliably detect this case and instead end up waiting for an EndOfTurn that never arrives.

Suggested fixes (any one)

  1. Add a ListenV2Warning type (type: Literal["Warning"], code: str, description: str) and include it in V2SocketClientResponse.
  2. Change AsyncV2SocketClient.__aiter__ to yield the raw dict (with a debug log) when the typed decode returns None, so consumers can still see it.
  3. At minimum, log the raw payload at INFO/WARNING when construct_type returns None, so the drop is observable in dev.

Workaround currently in use

Iterating socket._websocket directly and parsing raw JSON in the consumer. This works but couples client code to a private SDK attribute.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions