Skip to content

fix: handle APIError code 1000 in _receive_from_model for google-genai >= 1.62.0#4911

Closed
TonyLee-AI wants to merge 1 commit intogoogle:mainfrom
TonyLee-AI:fix/api-error-1000-normal-closure
Closed

fix: handle APIError code 1000 in _receive_from_model for google-genai >= 1.62.0#4911
TonyLee-AI wants to merge 1 commit intogoogle:mainfrom
TonyLee-AI:fix/api-error-1000-normal-closure

Conversation

@TonyLee-AI
Copy link
Copy Markdown

Problem

google-genai >= 1.62.0 changed live.py to catch ConnectionClosedOK inside _receive() and re-raise it as APIError(code=1000).

Because APIError is not a subclass of ConnectionClosedOK, it bypassed the existing handler in _receive_from_model:

except ConnectionClosedOK:
    pass  # APIError(1000) is NOT caught here

It then propagated to run_live()'s generic except Exception handler, which logged it as an unexpected error — even though WebSocket close code 1000 (RFC 6455 Normal Closure) indicates a clean, intentional session end.

With google-genai == 1.61.0 (which did not perform this conversion), the behaviour was silent and correct.

Fix

Catch APIError in _receive_from_model and treat code 1000 the same as ConnectionClosedOK — silently discard it. Any other APIError code is re-raised unchanged.

except ConnectionClosedOK:
    pass
except genai_errors.APIError as e:
    # google-genai >= 1.62.0 converts ConnectionClosedOK into APIError with
    # WebSocket close code 1000 (RFC 6455 Normal Closure). Treat it the same
    # as ConnectionClosedOK so that a clean session close does not propagate
    # as an unexpected error.
    if e.code != 1000:
        raise

Logs

Before (google-genai==1.68.0, unpatched) — triggered on every normal session close (e.g. call transfer):

ERROR:google_adk.google.adk.flows.llm_flows.base_llm_flow:An unexpected error occurred in live flow: 1000 None.
Traceback (most recent call last):
  File ".../google/genai/live.py", line 535, in _receive
    raw_response = await self._ws.recv(decode=False)
  File ".../websockets/asyncio/connection.py", line 322, in recv
    raise self.protocol.close_exc from self.recv_exc
websockets.exceptions.ConnectionClosedOK: sent 1000 (OK); then received 1000 (OK)
During handling of the above exception, another exception occurred:
  File ".../google/adk/flows/llm_flows/base_llm_flow.py", line 530, in run_live
    async for event in agen:
  ...
  File ".../google/genai/live.py", line 545, in _receive
    errors.APIError.raise_error(code, reason, None)
google.genai.errors.APIError: 1000 None.

After (google-genai==1.68.0, patched) — no error log on normal session close. The session ends silently as intended.

Testing plan

  • Added tests/unittests/flows/llm_flows/test_base_llm_flow_connection_close.py with 3 unit tests:
    • test_receive_from_model_connection_closed_ok_is_silent — existing behaviour unchanged
    • test_receive_from_model_api_error_1000_is_silent — new: APIError(1000) is swallowed
    • test_receive_from_model_api_error_non_1000_is_raised — non-1000 codes still propagate
  • All 3 tests pass:
PASSED test_receive_from_model_connection_closed_ok_is_silent
PASSED test_receive_from_model_api_error_1000_is_silent
PASSED test_receive_from_model_api_error_non_1000_is_raised
3 passed in 2.35s
  • Manual E2E: verified with google-genai==1.68.0 + google-adk==1.27.2 that normal session close (call transfer, hang-up) no longer produces ERROR: An unexpected error occurred in live flow: 1000 None.

@google-cla
Copy link
Copy Markdown

google-cla bot commented Mar 20, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adk-bot adk-bot added the live [Component] This issue is related to live, voice and video chat label Mar 20, 2026
@adk-bot
Copy link
Copy Markdown
Collaborator

adk-bot commented Mar 20, 2026

Response from ADK Triaging Agent

Hello @TonyLee-AI, thank you for creating this PR!

It looks like you haven't signed the Contributor License Agreement (CLA) yet. Please visit https://cla.developers.google.com/ to sign the agreement.

Also, for bug fixes like this one, our contribution guidelines require an associated GitHub issue. If one doesn't exist, could you please create one and link it to this PR?

This information will help reviewers to review your PR more efficiently. Thanks!

…i >= 1.62.0

google-genai >= 1.62.0 changed live.py to catch ConnectionClosedOK inside
_receive() and re-raise it as APIError(code=1000). Because APIError is not a
subclass of ConnectionClosedOK, it bypassed the existing
`except ConnectionClosedOK: pass` handler in _receive_from_model and
propagated to run_live()'s generic `except Exception` handler, which logged
it as an unexpected error even though WebSocket close code 1000 (RFC 6455
Normal Closure) indicates a clean session end.

Fix: catch APIError in _receive_from_model and treat code 1000 the same as
ConnectionClosedOK — silently discard it. Any other APIError code is
re-raised as before.
@TonyLee-AI TonyLee-AI force-pushed the fix/api-error-1000-normal-closure branch from b731b2e to 1af6892 Compare March 20, 2026 05:53
@TonyLee-AI TonyLee-AI closed this Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

live [Component] This issue is related to live, voice and video chat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants