Skip to content

KeyError: 'sys_tempc' when the NAS does not report a system temperature (faulty sensor, or model without one) #108

Description

@Alcotano

Summary

get_system_stats() reads the system temperature with direct key access:

"temp_c": int(root["sys_tempc"]),
"temp_f": int(root["sys_tempf"]),

When a QNAP does not report a system temperature, these keys are absent from
the response
and every call raises KeyError. That takes down the entire
library — disk, CPU, volume and network data included — even though the NAS
returns all of it correctly.

Two lines above, the same dict already guards an optional field:

root["cpu_model"] if "cpu_model" in root else None

The temperature fields did not get the same treatment.

This is not limited to old models

The unit I hit this on is a TS-253 Pro whose system temperature sensor is
faulty
(confirmed by the owner). The API response corroborates it: the device
advertises a system temperature sensor and defines thresholds for it, but cannot
read it, and several neighbouring hardware readings come back as error
sentinels.

That is the important part: the field disappears when the sensor fails, not
only on models that never had one.
Any QNAP can end up in this state. A unit
that works today will hard-fail the library the day its sensor dies — including
the TS-453E I mention below as the working case.

A failing hardware sensor should degrade one value. It should not make disk
temperatures, CPU load and volume usage unreachable.

Affected device

Model QNAP TS-253 Pro (modelName: TS-X53)
Platform TS-NASX86 / X86_BAYTRAIL, Intel Celeron J1900
Firmware QTS 5.2.10, build 3577 (2026-07-31)
Access HTTP, port 8080
Known hardware issue System temperature sensor faulty

Traceback

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/qnap/config_flow.py", line 64, in _async_validate_input
    stats = await self.hass.async_add_executor_job(api.get_system_stats)
  File "/usr/local/lib/python3.14/concurrent/futures/thread.py", line 86, in run
    result = ctx.run(self.task)
  File "/usr/local/lib/python3.14/concurrent/futures/thread.py", line 73, in run
    return fn(*args, **kwargs)
  File "/usr/local/lib/python3.14/site-packages/qnapstats/qnap_stats.py", line 232, in get_system_stats
    "temp_c": int(root["sys_tempc"]),
                  ~~~~^^^^^^^^^^^^^
KeyError: 'sys_tempc'

Evidence

Authentication and the request both succeed. I queried the exact endpoint the
library uses,
management/manaRequest.cgi?subfunc=sysinfo&hd=no&multicpu=1, and inspected
func > ownContent > root. It contains 87 fields, including everything the
library reads immediately before the failing line:

Field Value
server_name TUNANTE read OK
serial_number present read OK
cpu_model Intel(R) Celeron(R) CPU J1900 @ 2.00GHz read OK
cpu_tempc / cpu_tempf 48 / 118 read OK
system_temp_count 1 sensor is declared
SysTempWarnT / SysTempErrT 60 / 70 thresholds defined
sys_tempc absent crash
sys_tempf absent

Corroborating the faulty-sensor diagnosis, the surrounding hardware readings are
error sentinels rather than values:

Field Value Meaning
sysfan1 4294967295 0xFFFFFFFF, i.e. unreadable
sysfan1_stat -1 unknown
temp_reg_cnt / fan_reg_cnt 0 / 0 no readable registers
Power1Temp / PowerFanStatus1 -1 / -1 unknown

So the device declares the sensor, defines its thresholds, and then omits the
element entirely because it has no reading to give. The key is not empty — it is
not present at all.

For contrast, a TS-453E running the exact same firmware (QTS 5.2.10 build
3577), with a healthy sensor
, does include both fields and works flawlessly.
The presence of the field tracks sensor health, not firmware version.

The data that is being thrown away

The disk data is fully available on the failing device, via
disk/qsmart.cgi?func=all_hd_data:

<Temperature><oC><![CDATA[45]]></oC><oF><![CDATA[113]]></oF></Temperature>

Disk temperature is arguably the single most useful thing this library exposes —
it is what tells you a drive is cooking. Losing it because an unrelated
enclosure sensor is broken is the worst possible failure mode: monitoring
disappears exactly on the machine that already has a hardware problem.

Expected behaviour

A missing or unreadable system temperature should yield None for that one
value, while disk temperatures, CPU stats, volumes and network counters continue
to be returned normally.

Proposed fix

Apply the same defensive pattern already used for cpu_model:

-                "temp_c": int(root["sys_tempc"]),
-                "temp_f": int(root["sys_tempf"]),
+                "temp_c": int(root["sys_tempc"]) if "sys_tempc" in root else None,
+                "temp_f": int(root["sys_tempf"]) if "sys_tempf" in root else None,

Consumers already have to handle optional values elsewhere in this dict, so
returning None is consistent with the existing contract.

Downstream impact

This breaks the official Home Assistant QNAP integration on affected devices:
the config flow fails with a generic "Unexpected error" and the integration
cannot be added at all. The message gives no hint of the real cause, so it reads
like a connectivity or credentials problem — I spent a while checking ports,
SSL and passwords before reading the traceback.

Home Assistant core issue #80065
reports the same KeyError: 'sys_tempc'.

Related history

The same class of problem has been reported repeatedly for other optional
fields on other models:

  • #4KeyError: 'cpu_model' on TS-639
  • #8KeyError: 'cpu_tempc' on TS-410
  • #35KeyError on TS-251+

Given that any of these fields can vanish when the corresponding sensor fails, a
broader hardening pass over the optional fields in get_system_stats() would
likely prevent the next report of this kind.

I am happy to open a pull request with the change above if that helps.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions