weather_cache._fetch_weather_remote: агрегаты min/max/sum по daily-массивам падают на None-днях -> весь 7-day forecast молча теряется #1577

Closed
opened 2026-06-16 17:08:06 +00:00 by bot-backend · 0 comments
Collaborator

Severity: medium · Категория: edge-case / silent-data-loss · Файл: backend/app/services/weather_cache.py:133-161

Файл: backend/app/services/weather_cache.py:147-161 (массивы 133-138, ранний guard daily.time 130).

Суть: агрегаты min(t_min) (150), max(t_max) (151), sum(t_max)/len(t_max) (152), sum(t_min)/len(t_min) (153), max(uv) (157), max(wind_s) (161) защищены ТОЛЬКО if <list> else None — это проверка непустоты списка, а не отсутствия None-элементов. Open-Meteo штатно возвращает null для отдельных дней (uv_index_max, precipitation), при этом daily.time непустой — guard 130 не срабатывает. В Python 3.12 min/max/sum по списку с None бросают TypeError.

Доказательство намеренности: на тех же данных wind circular-mean (141-142, if d is not None) и precipitation_days (156, if p and ...) ЯВНО фильтруют None.

Импакт: TypeError ловится except Exception (169-171) -> None -> negative-cache 300с (_NEGATIVE_TTL_S=300) -> env_ok=False в parcels._build_environmental_score (bool(weather), docstring 26). Корректный прогноз (6/7 дней с данными) молча выбрасывается — тихая потеря данных погодного блока analyze.

Фикс: фильтровать None перед агрегацией, например vals = [x for x in t_min if x is not None] -> min(vals) if vals else None для t_min/t_max/uv/wind_s, либо ввести helper _safe_agg(seq, fn).

Почему баг: Один-единственный null-день в валидном 7-дневном прогнозе приводит к TypeError, который ловится широким except Exception (169-171) → функция возвращает None → get_weather_cached кладёт None в negative-cache на 5 минут. Полностью корректный прогноз (6 из 7 дней с данными) молча выбрасывается, и в _build_environmental_score env_ok становится False. Это тихая потеря данных основной фичи (погодный блок analyze), а не просто граничный отказ. Контракт «None допустим» маскирует баг — данные были, но не отдались.

Как чинить: Перед агрегатами отфильтровать None по каждому списку (как уже сделано для wind_d/precip), напр. t_min_v = [x for x in t_min if x is not None] и считать min/max/avg по очищенным спискам, с проверкой непустоты. Тогда частично-заполненный прогноз отдаёт реальные значения вместо полного отказа.


deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.85 · unit B13

**Severity:** medium · **Категория:** edge-case / silent-data-loss · **Файл:** `backend/app/services/weather_cache.py:133-161` Файл: backend/app/services/weather_cache.py:147-161 (массивы 133-138, ранний guard daily.time 130). Суть: агрегаты `min(t_min)` (150), `max(t_max)` (151), `sum(t_max)/len(t_max)` (152), `sum(t_min)/len(t_min)` (153), `max(uv)` (157), `max(wind_s)` (161) защищены ТОЛЬКО `if <list> else None` — это проверка непустоты списка, а не отсутствия None-элементов. Open-Meteo штатно возвращает null для отдельных дней (uv_index_max, precipitation), при этом daily.time непустой — guard 130 не срабатывает. В Python 3.12 min/max/sum по списку с None бросают TypeError. Доказательство намеренности: на тех же данных wind circular-mean (141-142, `if d is not None`) и precipitation_days (156, `if p and ...`) ЯВНО фильтруют None. Импакт: TypeError ловится `except Exception` (169-171) -> None -> negative-cache 300с (_NEGATIVE_TTL_S=300) -> env_ok=False в parcels._build_environmental_score (bool(weather), docstring 26). Корректный прогноз (6/7 дней с данными) молча выбрасывается — тихая потеря данных погодного блока analyze. Фикс: фильтровать None перед агрегацией, например `vals = [x for x in t_min if x is not None]` -> `min(vals) if vals else None` для t_min/t_max/uv/wind_s, либо ввести helper `_safe_agg(seq, fn)`. **Почему баг:** Один-единственный null-день в валидном 7-дневном прогнозе приводит к TypeError, который ловится широким `except Exception` (169-171) → функция возвращает None → get_weather_cached кладёт None в negative-cache на 5 минут. Полностью корректный прогноз (6 из 7 дней с данными) молча выбрасывается, и в _build_environmental_score env_ok становится False. Это тихая потеря данных основной фичи (погодный блок analyze), а не просто граничный отказ. Контракт «None допустим» маскирует баг — данные были, но не отдались. **Как чинить:** Перед агрегатами отфильтровать None по каждому списку (как уже сделано для wind_d/precip), напр. `t_min_v = [x for x in t_min if x is not None]` и считать min/max/avg по очищенным спискам, с проверкой непустоты. Тогда частично-заполненный прогноз отдаёт реальные значения вместо полного отказа. --- <sub>deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.85 · unit B13</sub>
bot-backend added the
week ревью 1
label 2026-06-16 17:08:06 +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#1577
No description provided.