{"record":{"id":"ee05b8315cf1ae5e","repo":"henrygd/beszel","slug":"panic-v","errorCode":null,"errorMessage":"panic: %v","messagePattern":"panic: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"agent/sensors.go","lineNumber":167,"sourceCode":"\t\tif !isValidSensor(sensorName, a.sensorConfig) {\n\t\t\tcontinue\n\t\t}\n\t\t// set dashboard temperature\n\t\tswitch a.sensorConfig.primarySensor {\n\t\tcase \"\":\n\t\t\ta.systemInfo.DashboardTemp = max(a.systemInfo.DashboardTemp, sensor.Temperature)\n\t\tcase sensorName:\n\t\t\ta.systemInfo.DashboardTemp = sensor.Temperature\n\t\t}\n\t\tsystemStats.Temperatures[sensorName] = utils.TwoDecimals(sensor.Temperature)\n\t}\n}\n\n// getTempsWithPanicRecovery wraps sensors.TemperaturesWithContext to recover from panics (gopsutil/issues/1832)\nfunc (a *Agent) getTempsWithPanicRecovery(getTemps getTempsFn) (temps []sensors.TemperatureStat, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"panic: %v\", r)\n\t\t}\n\t}()\n\t// get sensor data (error ignored intentionally as it may be only with one sensor)\n\ttemps, _ = getTemps(a.sensorConfig.context)\n\treturn\n}\n\nfunc (a *Agent) getTempsWithTimeout(getTemps getTempsFn) ([]sensors.TemperatureStat, error) {\n\ttype result struct {\n\t\ttemps []sensors.TemperatureStat\n\t\terr   error\n\t}\n\n\t// Use a longer timeout on the first run to allow for initialization\n\t// (e.g. Windows LHM subprocess startup)\n\ttimeout := a.sensorConfig.timeout\n\tif a.sensorConfig.firstRun {\n\t\ta.sensorConfig.firstRun = false","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/sensors.go#L149-L185","documentation":"The agent wraps gopsutil's sensors.TemperaturesWithContext in a panic-recovering helper because gopsutil can panic while parsing sensor data (gopsutil issue #1832). This error converts that panic into a normal error carrying the panic value.","triggerScenarios":"getTempsWithPanicRecovery calls getTemps(a.sensorConfig.context); the underlying gopsutil implementation panics (nil map/deref in sensor parsing on certain kernels/boards), the deferred recover() fires and err = fmt.Errorf(\"panic: %v\", r).","commonSituations":"gopsutil version with the sensor-parsing bug on specific Linux hardware/DMI tables; unusual sysfs hwmon layouts (SBCs, some server boards); corrupted or oddly-formatted temperature sensor files.","solutions":["Upgrade gopsutil (and beszel agent) to a version past the panic fixed upstream (gopsutil/issues/1832).","If only sensor temps fail, ignore — the agent continues and simply reports no temperature data.","Check hwmon files under /sys/class/hwmon for malformed content and mask the offending module if needed.","Pin to a known-good gopsutil version if you build beszel from source and the latest still panics."],"exampleFix":"// before\nrequire github.com/shirou/gopsutil/v3 v3.23.8 // panics in sensors\n// after\nrequire github.com/shirou/gopsutil/v3 v3.24.x // includes sensors panic fix","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"temps, err := a.getTempsWithPanicRecovery(sensors.TemperaturesWithContext)\nif err != nil {\n    slog.Warn(\"sensor read failed (recovered)\", \"err\", err)\n    temps = nil // continue without temperature data\n}","preventionTips":["Keep gopsutil updated to a release containing the sensors panic fix (issue #1832)","Keep the panic-recovery wrapper in place rather than calling gopsutil directly","Pin and test gopsutil versions when building beszel from source","Alert on repeated recovered panics — they indicate a real upstream bug"],"tags":["sensors","panic","gopsutil","temperature"],"backgroundTag":"third-party-panic-recovered","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}