Skip to content

fix: keep frame decoding alive on non-dict payload#67

Closed
MikeMike88 wants to merge 1 commit into
MaxApiTeam:mainfrom
MikeMike88:fix/inbound-frame-non-dict-payload
Closed

fix: keep frame decoding alive on non-dict payload#67
MikeMike88 wants to merge 1 commit into
MaxApiTeam:mainfrom
MikeMike88:fix/inbound-frame-non-dict-payload

Conversation

@MikeMike88

@MikeMike88 MikeMike88 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Описание

Некоторые входящие фреймы несут payload не в виде map, а голым значением —
например, ответ-ошибка приходит числовым кодом. Поле InboundFrame.payload
типизировано как dict | None, поэтому создание фрейма падало с
ValidationError прямо в цикле приёма (_recv_loop). Это исключение
обрабатывается как фатальная ошибка соединения → разрыв и реконнект, а тот же
фрейм приходит снова → цикл реконнектов.

Изменения:

  • payload остаётся dict | None (консьюмеры читают его именно так и уже
    гуардят None);
  • исходное декодированное значение кладётся в raw (теперь Any), чтобы не
    терять данные (тот самый код ошибки) и не ронять приём кадров.

Обратной несовместимости нет: для обычных dict-фреймов payload и raw
ведут себя как раньше; поле InboundFrame.raw в коде нигде не читается.

Тип изменений

  • Исправление бага
  • Новая функциональность
  • Улучшение документации
  • Рефакторинг

Связанные задачи / Issue

Ссылка на issue, если есть: #

Тестирование

Добавлен регрессионный тест test_tcp_protocol_keeps_non_dict_payload_in_raw
в tests/protocol/test_protocols.py:

import msgpack
from pymax.protocol import Command, Opcode
from pymax.protocol.tcp.framing import TcpPacketFramer
from pymax.protocol.tcp.protocol import TcpProtocol

p = TcpProtocol()
raw = TcpPacketFramer().pack(
    ver=p.version, cmd=Command.ERROR, seq=5, opcode=Opcode.LOGIN,
    flags=0, payload_bytes=msgpack.packb(-12, use_bin_type=True),
)
frame = p.decode(raw)        # раньше: ValidationError
assert frame.payload is None
assert frame.raw == -12

Запуск:

pytest tests/protocol/test_protocols.py

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced protocol message handling to properly preserve and process non-dictionary responses, including numeric codes, error values, and other non-structured data. These values are now correctly distinguished from structured payloads and properly stored, preventing potential data loss and processing errors when receiving such responses from the system.

Some inbound frames carry a bare scalar payload instead of a map
(e.g. an error response comes as a numeric code). `InboundFrame.payload`
is typed `dict | None`, so constructing the frame raised a
`ValidationError` right in the receive loop, which tore down the
connection and triggered a reconnect loop.

Keep `payload` as `dict | None` (consumers read it that way) and store
the original decoded value in `raw` (now `Any`) so such frames no longer
crash frame decoding. Add a regression test for a scalar payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb457e1b-a4fa-4acf-b460-a6a5a86e462f

📥 Commits

Reviewing files that changed from the base of the PR and between ff3af5b and b2486e3.

📒 Files selected for processing (3)
  • src/pymax/protocol/models.py
  • src/pymax/protocol/tcp/protocol.py
  • tests/protocol/test_protocols.py

📝 Walkthrough

Walkthrough

InboundFrame.raw is widened from dict[Any, Any] | None to Any. TcpProtocol.decode now stores the decoded result in an intermediate variable, assigns payload only when it is a dict, logs non-dict values at debug level, and always stores the full decoded value in raw. A new test covers the non-dict scalar case.

Changes

Non-dict TCP payload handling

Layer / File(s) Summary
InboundFrame.raw type widening and decode routing
src/pymax/protocol/models.py, src/pymax/protocol/tcp/protocol.py
InboundFrame.raw type changes from dict[Any, Any] | None to Any with explanatory comments. TcpProtocol.decode introduces a decoded intermediate, sets payload = decoded only for dict values (otherwise None with a debug log), and always passes decoded into raw.
Test: non-dict scalar payload preserved in raw
tests/protocol/test_protocols.py
New test test_tcp_protocol_keeps_non_dict_payload_in_raw builds a TCP frame with an integer Msgpack payload, decodes it, and asserts payload is None, raw equals -12, and header fields are correct.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐇 Hopped through bytes both map and plain,
A number came — not dict-shaped frame!
I tucked it safe in raw instead,
payload set to None ahead.
No crash, just a debug note to say:
"Scalar arrived — and that's okay!" 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: preventing frame decoding failures when non-dictionary payloads arrive, which directly matches the changeset.
Description check ✅ Passed The description follows the template structure completely, includes all required sections with detailed explanations, identifies the bug type, provides testing examples, and explains both the problem and solution clearly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ink-developer

Copy link
Copy Markdown
Collaborator

Проблема была в декодере PyMax.
PR не закрывает главную проблему (неправильную декомпрессию)
Будет исправлено в 2.3.1

@ink-developer ink-developer mentioned this pull request Jun 19, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants