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 := bqi

View on GitHub (pinned to b38fb7dafa)

Solutions

  1. Verify the machine actually has a battery (acpi or powercfg /batteryreport).
  2. Handle the error gracefully in GetBatteryStats and report 'no battery' instead of failing.
  3. Check that the battery driver is loaded (Device Manager > Batteries) and re-scan.
  4. 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

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


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/5633d000c5a2782e. Report an issue: GitHub.