(low) _fmt_number падает на NaN/Inf-float (ValueError в детерминированном fallback-пути chat) #1396

Closed
opened 2026-06-14 18:11:24 +00:00 by bot-backend · 0 comments
Collaborator

Описание

В _fmt_number ветка float делает if value == int(value) (строка 153). Для float('nan')/float('inf') вызов int(value) бросает ValueError. Это исключение всплывёт из render_answer → детерминированного Step-1 fallback-пути, который по контракту проекта обязан НИКОГДА не падать (эндпоинт chat ловит только сбой чтения отчёта, но не сбой рендера). Результат — HTTP 500 вместо ответа.

Корень

_fmt_number не является total-функцией: float-ветка не отсеивает не-конечные значения перед int(value). Достижимость заблокирована внешним гейтом: report_dict приходит из run.result (JSONB), а PostgreSQL JSONB отвергает NaN/Infinity при касте, поэтому персистентный отчёт их физически не содержит. Защита де-факто есть, но она внешняя и неочевидная — сам код контракт fallback не держит.

Evidence

intents.py:152-156:

if isinstance(value, float):
    if value == int(value):   # int(float('nan')) -> ValueError
        return _fmt_thousands(value)
    return repr(value).replace(".", ",")

Suggested fix

В начале float-ветки отсеять не-конечные числа: import math; if not math.isfinite(value): return None (или return str(value)), чтобы _fmt_number оставался total-функцией и fallback-путь не мог упасть.

Severity-обоснование

low: баг в коде реален (total-функция нарушена), но не эксплуатируем в текущем проде — единственный источник report_dict (чтение из analysis_runs.result JSONB) проходит через PostgreSQL-валидацию JSONB, отвергающую NaN/Infinity. Защита fallback-контракта от любого мусорного числа всё же отсутствует на уровне самого кода.


week ревью 1 · multi-agent аудит 2026-06-14 · severity=low · category=error-handling

Затронутые файлы:

  • /Users/anton/Птица/gendesign/backend/app/services/chat/intents.py
## Описание В `_fmt_number` ветка float делает `if value == int(value)` (строка 153). Для `float('nan')`/`float('inf')` вызов `int(value)` бросает `ValueError`. Это исключение всплывёт из `render_answer` → детерминированного Step-1 fallback-пути, который по контракту проекта обязан НИКОГДА не падать (эндпоинт chat ловит только сбой чтения отчёта, но не сбой рендера). Результат — HTTP 500 вместо ответа. ## Корень `_fmt_number` не является total-функцией: float-ветка не отсеивает не-конечные значения перед `int(value)`. Достижимость заблокирована внешним гейтом: `report_dict` приходит из `run.result` (JSONB), а PostgreSQL JSONB отвергает NaN/Infinity при касте, поэтому персистентный отчёт их физически не содержит. Защита де-факто есть, но она внешняя и неочевидная — сам код контракт fallback не держит. ## Evidence `intents.py:152-156`: ```python if isinstance(value, float): if value == int(value): # int(float('nan')) -> ValueError return _fmt_thousands(value) return repr(value).replace(".", ",") ``` ## Suggested fix В начале float-ветки отсеять не-конечные числа: `import math; if not math.isfinite(value): return None` (или `return str(value)`), чтобы `_fmt_number` оставался total-функцией и fallback-путь не мог упасть. ## Severity-обоснование **low**: баг в коде реален (total-функция нарушена), но не эксплуатируем в текущем проде — единственный источник `report_dict` (чтение из `analysis_runs.result` JSONB) проходит через PostgreSQL-валидацию JSONB, отвергающую NaN/Infinity. Защита fallback-контракта от любого мусорного числа всё же отсутствует на уровне самого кода. --- _week ревью 1 · multi-agent аудит 2026-06-14 · severity=**low** · category=`error-handling`_ Затронутые файлы: - `/Users/anton/Птица/gendesign/backend/app/services/chat/intents.py`
bot-backend added the
week ревью 1
label 2026-06-14 18:11:24 +00:00
bot-backend added the
scope/backend
label 2026-06-15 11:01:58 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1396
No description provided.