Skip to content

MessageParseError on rate_limit_event crashes receive_messages() generator #583

Description

@thepaul

parse_message() raises MessageParseError("Unknown message type: rate_limit_event") when the Claude Code CLI emits a rate_limit_event message. Because this exception is raised inside the receive_messages() async generator (at the yield parse_message(data) in client.py:187), it terminates the generator entirely. The caller can never receive any further messages. The session is effectively dead.

Reproduction

import asyncio
from claude_agent_sdk import ClaudeAgent, ClaudeAgentOptions

async def main():
    options = ClaudeAgentOptions(workdir=".")
    client = ClaudeAgent(options)
    await client.connect()
    await client.query("Hello")

    async for message in client.receive_messages():
        print(message)
        # If the API returns a rate_limit_event during the stream,
        # this raises MessageParseError and the generator dies.
        # No further messages are ever received.

asyncio.run(main())

Root cause

message_parser.py has a strict allowlist of message types:

match message_type:
    case "user": ...
    case "assistant": ...
    case "system": ...
    case "result": ...
    case "stream_event": ...
    case _:
        raise MessageParseError(f"Unknown message type: {message_type}", data)

The Claude Code CLI now emits rate_limit_event messages, which aren't in this list.

Impact

This is particularly severe because:

  1. The error occurs inside the yield expression in receive_messages(), which kills the async generator, and it cannot be resumed.
  2. Rate limit events are transient and informational. They should not be fatal.
  3. Any new message type added to the CLI in the future will trigger the same crash, making the SDK fragile against CLI updates.

Suggested fix

Two changes would make this robust:

  1. Add rate_limit_event as a recognized type in message_parser.py (with an appropriate dataclass).
  2. Make unknown types non-fatal for forward compatibility. Either:
  • Return a generic UnknownMessage wrapper instead of raising, or
  • Catch MessageParseError inside receive_messages() and skip/log the message:
async def receive_messages(self):
    async for data in self._stream:
        try:
            msg = parse_message(data)
        except MessageParseError:
            continue
        yield msg

Environment

  • claude-agent-sdk==0.1.37
  • Claude Code CLI 2.1.45
  • Python 3.13.12
  • macOS 15.5

Workaround

Monkey-patch parse_message to return None for unknown types and filter in the consumer:

import claude_agent_sdk._internal.message_parser as mp
_original = mp.parse_message

def _tolerant_parse(data):
    try:
        return _original(data)
    except mp.MessageParseError as e:
        if "Unknown message type" in str(e):
            return None
        raise

mp.parse_message = _tolerant_parse

# Also patch the client module's imported reference
import claude_agent_sdk.client as client_mod
if hasattr(client_mod, 'parse_message'):
    client_mod.parse_message = _tolerant_parse

Activity

  1. added a commit that references this issue on Feb 19, 2026
    ba362f2
  2. added 3 commits that reference this issue on Feb 20, 2026
    8c86d2c
    5b5bead
    146e3d6
  3. added 3 commits that reference this issue on Mar 7, 2026
    caebb82
    bdc6270
    2d5c3cb
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