|
| 1 | +"""Repro test: rate_limit_event message type crashes the Python Agent SDK. |
| 2 | +
|
| 3 | +CLI v2.1.45+ emits `rate_limit_event` messages when rate limit status changes |
| 4 | +for claude.ai subscription users. The Python SDK's message parser had no handler |
| 5 | +for this message type, causing a MessageParseError crash. |
| 6 | +
|
| 7 | +Fix: the parser now returns None for unknown message types, and the caller |
| 8 | +filters them out. This makes the SDK forward-compatible with new CLI message types. |
| 9 | +
|
| 10 | +See: https://github.com/anthropics/claude-agent-sdk-python/issues/583 |
| 11 | +""" |
| 12 | + |
| 13 | +from claude_agent_sdk._internal.message_parser import parse_message |
| 14 | + |
| 15 | + |
| 16 | +class TestRateLimitEventHandling: |
| 17 | + """Verify rate_limit_event and unknown message types don't crash.""" |
| 18 | + |
| 19 | + def test_rate_limit_event_returns_none(self): |
| 20 | + """rate_limit_event should be silently skipped, not crash.""" |
| 21 | + data = { |
| 22 | + "type": "rate_limit_event", |
| 23 | + "rate_limit_info": { |
| 24 | + "status": "allowed_warning", |
| 25 | + "resetsAt": 1700000000, |
| 26 | + "rateLimitType": "five_hour", |
| 27 | + "utilization": 0.85, |
| 28 | + "isUsingOverage": False, |
| 29 | + }, |
| 30 | + "uuid": "550e8400-e29b-41d4-a716-446655440000", |
| 31 | + "session_id": "test-session-id", |
| 32 | + } |
| 33 | + |
| 34 | + result = parse_message(data) |
| 35 | + assert result is None |
| 36 | + |
| 37 | + def test_rate_limit_event_rejected_returns_none(self): |
| 38 | + """Hard rate limit (status=rejected) should also be skipped.""" |
| 39 | + data = { |
| 40 | + "type": "rate_limit_event", |
| 41 | + "rate_limit_info": { |
| 42 | + "status": "rejected", |
| 43 | + "resetsAt": 1700003600, |
| 44 | + "rateLimitType": "seven_day", |
| 45 | + "isUsingOverage": False, |
| 46 | + "overageStatus": "rejected", |
| 47 | + "overageDisabledReason": "out_of_credits", |
| 48 | + }, |
| 49 | + "uuid": "660e8400-e29b-41d4-a716-446655440001", |
| 50 | + "session_id": "test-session-id", |
| 51 | + } |
| 52 | + |
| 53 | + result = parse_message(data) |
| 54 | + assert result is None |
| 55 | + |
| 56 | + def test_unknown_message_type_returns_none(self): |
| 57 | + """Any unknown message type should return None for forward compatibility.""" |
| 58 | + data = { |
| 59 | + "type": "some_future_event_type", |
| 60 | + "uuid": "770e8400-e29b-41d4-a716-446655440002", |
| 61 | + "session_id": "test-session-id", |
| 62 | + } |
| 63 | + |
| 64 | + result = parse_message(data) |
| 65 | + assert result is None |
| 66 | + |
| 67 | + def test_known_message_types_still_parsed(self): |
| 68 | + """Known message types should still be parsed normally.""" |
| 69 | + data = { |
| 70 | + "type": "assistant", |
| 71 | + "message": { |
| 72 | + "content": [{"type": "text", "text": "hello"}], |
| 73 | + "model": "claude-sonnet-4-6-20250929", |
| 74 | + }, |
| 75 | + } |
| 76 | + |
| 77 | + result = parse_message(data) |
| 78 | + assert result is not None |
| 79 | + assert result.content[0].text == "hello" |
0 commit comments