henrygd/beszel · warning
battery tag not returned
Error message
battery tag not returned
What it means
winBatteryGet on Windows first queries the battery query information (IOCTL_BATTERY_QUERY_TAG) to obtain a battery tag that identifies the battery device for subsequent calls. If the DeviceIoControl call fails or the returned tag is 0, the library cannot address the battery and throws this error. Tag 0 means no battery is present or the driver did not assign a tag.
Source
Thrown at agent/battery/battery_windows.go:216
if err != nil {
return Battery{}, err
}
defer windows.CloseHandle(handle)
var dwOut uint32
var dwWait uint32
var bqi batteryQueryInformation
err = windows.DeviceIoControl(
handle,
2703424, // IOCTL_BATTERY_QUERY_TAG
(*byte)(unsafe.Pointer(&dwWait)),
uint32(unsafe.Sizeof(dwWait)),
(*byte)(unsafe.Pointer(&bqi.BatteryTag)),
uint32(unsafe.Sizeof(bqi.BatteryTag)),
&dwOut, nil,
)
if err != nil || bqi.BatteryTag == 0 {
return Battery{}, errors.New("battery tag not returned")
}
var bi batteryInformation
if err = windows.DeviceIoControl(
handle,
2703428, // IOCTL_BATTERY_QUERY_INFORMATION
(*byte)(unsafe.Pointer(&bqi)),
uint32(unsafe.Sizeof(bqi)),
(*byte)(unsafe.Pointer(&bi)),
uint32(unsafe.Sizeof(bi)),
&dwOut, nil,
); err != nil {
return Battery{}, err
}
// BatteryDeviceName is optional, so retain the deterministic fallback on error.
name := ""
nameQuery := bqiView on GitHub (pinned to b38fb7dafa)
Solutions
- Verify the machine actually has a battery (acpi or powercfg /batteryreport).
- Handle the error gracefully in GetBatteryStats and report 'no battery' instead of failing.
- Check that the battery driver is loaded (Device Manager > Batteries) and re-scan.
- Retry the device enumeration; tags are assigned at query time and can change when batteries are added/removed.
Example fix
// before
b, err := GetBatteryStats()
// after
b, err := GetBatteryStats()
if err != nil && strings.Contains(err.Error(), "battery tag not returned") {
log.Info("no battery present, skipping battery reporting")
return nil
} Defensive patterns
Strategy: fallback
Validate before calling
// before calling GetBatteryStats on Windows
if _, err := os.Stat(`\\.\Battery0`); err != nil {
log.Info("no battery device present, skipping battery stats")
return
} Type guard
func isNoBatteryErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "battery tag not returned")
} Try / catch
b, err := GetBatteryStats()
if err != nil {
if isNoBatteryErr(err) {
reportBatteryUnavailable() // fall back to 'no battery' telemetry
return
}
return err
} Prevention
- Skip battery collection on machines known to have no battery (desktops, VMs).
- Check Device Manager / WMI Win32_Battery presence before querying raw IOCTLs.
- Re-enumerate battery devices periodically: battery tags can change.
- Log the underlying DeviceIoControl error code for diagnostics.
When it happens
Trigger: The device path opened from \Device\HarddiskVolumeShadowCopy*/battery or similar does not expose a valid battery interface, or the machine has no battery, or the battery driver failed to return a tag via IOCTL_BATTERY_QUERY_TAG (DeviceIoControl error or BatteryTag == 0).
Common situations: Running the agent on a desktop PC or VM without a battery; querying a battery device path after the battery was removed; running on a system where the battery driver (CmBatt/Battery) is not loaded; containers or services without access to the device.
Related errors
- battery capacity unknown
- no sensors found (try running as admin with LHM=true)
- failed to create temp directory: %w
- failed to create smartctl directory: %w
- failed to write embedded smartctl: %w
AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31).
Data as JSON: /api/errors/5633d000c5a2782e.
Report an issue: GitHub.