Accept a null errorCode instead of losing the whole device - #131
Conversation
The cloud returns "error": {"errorCode": null} on some devices.
DeviceErrorSummary typed the field ErrorCode | int, and neither union
branch accepts None, so a null failed validation of the entire Device -
taking the whole /device/list response with it. Downstream that meant the
device disappeared and the Home Assistant integration could not set up at
all (eman/ha_nwp500#131); the block was unmodelled before 9.3.1, which is
why rolling back restored service.
Type it ErrorCode | int | None, defaulting to None. Mapping null to 0
would assert the device is fault-free, which is not what a null says: the
cloud reported no code. Both known consumers already treat a missing code
as nothing to report.
Fixes #130
Claude-Session: https://claude.ai/code/session_01LhLMPrLpPJ56jUcQmiBh1d
There was a problem hiding this comment.
🟡 Changes recommended
The OpenAPI 3.1 nullability syntax and public Python model documentation need correction.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Allows REST devices with null or missing errorCode values to remain parseable.
Changes:
- Makes
DeviceErrorSummary.error_codenullable. - Adds null and absent-field tests.
- Updates changelog and OpenAPI documentation.
File summaries
| File | Description |
|---|---|
src/nwp500/models/device.py |
Adds nullable error-code handling. |
tests/test_device_rest_models.py |
Tests null and missing codes. |
docs/openapi.yaml |
Documents nullability. |
CHANGELOG.rst |
Records the fix. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| error_code: ErrorCode | int | None = Field( | ||
| default=None, union_mode="left_to_right" |
There was a problem hiding this comment.
Fixed in 95e2232 — the field list now reads ErrorCode | int | None and explains that a null is "the cloud reported no code", not NO_ERROR. The example was worse than stale: error_code != ErrorCode.NO_ERROR is true for None, so it would have printed code None as a fault. It now pulls the code out first and guards on code is not None.
| errorCode: | ||
| type: integer | ||
| nullable: true |
There was a problem hiding this comment.
Correct, and it applied to more than this line — nullable was used 15 times in the file, all predating this PR, so every one of them was being ignored by 3.1 tooling. Fixing only errorCode would have left it inconsistent with errorOccuredTime two lines below it, so 95e2232 converts all 15 to JSON Schema type arrays (type: [integer, "null"]). The file still parses as valid YAML and declares 3.1.0.
Two follow-ups from review: OpenAPI 3.1 dropped `nullable` as a schema keyword, so 3.1 tooling read `errorCode` as integer-only and the annotation did nothing. Express it as a JSON Schema type array instead. The file had 15 of these, all predating this change; converting only the new one would have left the same block inconsistent with itself, so all 15 are converted. `docs/reference/python_api/models.rst` still documented the field as `ErrorCode | int`, and its example tested `error_code != NO_ERROR`, which is true for None - it would have reported "code None" as a fault. Both now describe the nullable contract. Claude-Session: https://claude.ai/code/session_01LhLMPrLpPJ56jUcQmiBh1d
Fixes #130 — reported downstream as eman/ha_nwp500#131.
The cloud returns
"error": {"errorCode": null}on some devices.DeviceErrorSummary.error_code, added in 9.3.1 (#124), is typedErrorCode | intwithunion_mode="left_to_right"; neither branch acceptsNone, so a null fails validation of the entireDeviceand takes the whole/device/listresponse with it. Downstream the device simply disappears and the Home Assistant integration cannot set up:Every other field added alongside it (
errorOccuredTime, thedescalingtimestamps,modelTypeCode,installerId) is already null-tolerant;errorCodeis the one that wasn't.Fix: type it
ErrorCode | int | None, defaulting toNone. Mapping null to0would assert the device is fault-free, which is not what a null says — the cloud reported no code. Both known consumers (ha_nwp500'scloud_error_codesensor and its diagnostics) already treat a missing code as nothing to report, so no downstream change is needed beyond the version pin.Two tests cover it: an explicit
null, and anerrorblock with noerrorCodekey at all.docs/openapi.yamlmarks the field nullable to match.Note for anyone running the suite locally: the checked-in
venv/is Python 3.13 and can no longer import the package (converters.pyuses PEP 758 unparenthesizedexcept, and the project targets 3.14). Full suite passes on 3.14 — 737 tests.