fix(connection): ретраить 429 и не падать на не-JSON теле ошибки - #188
Open
Fgeeha wants to merge 1 commit into
Open
fix(connection): ретраить 429 и не падать на не-JSON теле ошибки#188Fgeeha wants to merge 1 commit into
Fgeeha wants to merge 1 commit into
Conversation
MAX отвечает на превышение лимита запросов статусом 429 с заголовком
Content-Type: application/octet-stream. Статус не входил в retry_on_statuses,
а разбор тела шёл через строгий response.json(), поэтому вызов падал с
ContentTypeError вместо MaxApiError с кодом 429 — отличить лимит запросов от
сбоя разбора ответа было нельзя.
Теперь 429 входит в DEFAULT_RETRY_STATUSES и повторяется тем же backoff, что
и 5xx, а тело ошибочного ответа читается без опоры на Content-Type: не-JSON
тело отдаётся как {"error": <текст>}.
Contributor
There was a problem hiding this comment.
Pull request overview
Добавляет устойчивую обработку временных HTTP-ошибок и ответов с не-JSON телом.
Changes:
- Добавлен автоматический retry для HTTP 429.
- Ошибочные ответы разбираются независимо от
Content-Type. - Добавлены тесты обработки rate limit.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
maxapi/connection/base.py |
Обрабатывает retry и не-JSON ошибки. |
maxapi/client/default.py |
Добавляет 429 в статусы retry. |
tests/test_retry.py |
Покрывает новую обработку 429. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| response: Ответ с не-2xx статусом. | ||
|
|
||
| Returns: | ||
| Any: Разобранный JSON либо ``{"error": <текст ответа>}``. |
| await base.request(method=HTTPMethod.GET, path="/test") | ||
|
|
||
| assert exc_info.value.code == 400 | ||
| assert exc_info.value.raw == {"error": "rate limit exceeded"} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Проблема
MAX отвечает на превышение лимита запросов статусом 429 с заголовком
Content-Type: application/octet-stream. ВBaseConnection.request()этоприводит к двум неприятностям сразу:
retry_on_statuses(по умолчанию 502, 503, 504), поэтомуответ считается окончательным и не повторяется — хотя это ровно тот случай,
для которого механизм повторов и предназначен;
await response.json(), который наapplication/octet-streamподнимаетaiohttp.ContentTypeError. Вызывающийкод получает ошибку разбора ответа вместо
MaxApiErrorс кодом 429 и неможет отличить лимит запросов от сбоя в самой библиотеке.
На практике это заставляет прикладной код патчить
BaseConnection.request,чтобы ловить
ContentTypeErrorи трактовать его как 429, — что явно не то,чем должен заниматься пользователь SDK.
Решение
DEFAULT_RETRY_STATUSES = (429, 502, 503, 504)— 429 повторяется тем жеbackoff, что и серверные ошибки; поведение настраивается как раньше, через
DefaultConnectionProperties(retry_on_statuses=...);_read_error_payload(): сначалаresponse.json(content_type=None), при неудаче —{"error": <текст ответа>}.Успешный путь остаётся строгим;
_RetryableServerErrorдля 429 — «Rate limit 429» вместо«Server error 429», лог повторов использует текст исключения.
Совместимость
Меняется значение по умолчанию: раньше 429 отдавался вызывающему коду сразу
(точнее, падал с
ContentTypeError), теперь запрос повторяется доmax_retriesраз. Кто хочет прежнее поведение — передаётretry_on_statuses=(502, 503, 504).Тесты
tests/test_retry.py, классTestRateLimitStatus— 3 теста:MaxApiErrorсcode == 429;{"error": <текст>}, а не роняет вызов.Плюс обновлены два существующих ассерта на состав
DEFAULT_RETRY_STATUSES.Локально:
pytest -q— 957 passed, 12 skipped;mypy maxapi— чисто;ruff format --check— чисто.