I build a small Windows diagnostic tool. One of its rules looked at the System event log, found Kernel-Processor-Power Event ID 37, and told the user: "your cooling system is probably the problem."
Then I ran it on my own PC, which has no cooling problem. It told me my cooling was the problem.
This post is about why Event 37 is a terrible proxy for overheating, and the three filters that fixed the false positive.
What Event 37 actually says
Event ID 37 from Microsoft-Windows-Kernel-Processor-Power reads roughly: the speed of processor N is being limited by system firmware.
Heat is one reason firmware does that. It is not the only one. A power limit, a weak charger, a battery state or an OEM power profile produce the same event. The event tells you a limit was applied. It does not tell you why.
My first version of the rule was simple:
- count Event 37 over the last 14 days
- ignore the ones right after the latest boot
- if three or more remain, flag "physical cooling problem"
It was also compared with the CPU load measured during an 8-second scan, taken while the user is idle by definition. Two things measured at two different times, used as one argument.
The numbers on my machine
Over 14 days my log had 12 Event 37 entries. The old rule kept 11 of them as "not at boot" and concluded the worst.
Looking at them one by one:
- 8 of the 12 happened within 10 minutes after a boot. Not just the latest boot: any boot in the period.
- The other 4 were not heat spikes at all. Each reported a cap lasting 33 to 48 hours. That is a permanent power ceiling, not a thermal event.
Real overheating episodes: zero.
Fix 1: ignore every boot, not just the last one
Firmware often applies a limit while the system starts. Collect every boot in the window with Event 12 from Kernel-General, then drop any Event 37 that lands in the 10 minutes after one.
$boots = Get-WinEvent -FilterHashtable @{
LogName='System'; ProviderName='Microsoft-Windows-Kernel-General'; Id=12
StartTime=(Get-Date).AddDays(-15)
} | ForEach-Object { $_.TimeCreated }
foreach ($e in $ev37) {
$atBoot = $boots | Where-Object {
$d = ($e.TimeCreated - $_).TotalMinutes; $d -ge 0 -and $d -le 10
}
if ($atBoot) { continue }
# ...next filters
}
Fix 2: only count events on mains power
On battery, limiting the CPU is intended behaviour. Kernel-Power Event 105 records power source changes, and its AcOnline field says whether the machine was plugged in. For each Event 37, take the latest Event 105 before it. If the machine was on battery, it is not evidence of anything.
Fix 3: use the duration, not just the count
Event 37 carries a CapDurationInSeconds field. A thermal throttle comes and goes in minutes. A cap that lasts a day or more is a configuration, not a symptom. I treat anything of an hour or longer as a permanent cap and drop it.
$xml = [xml]$e.ToXml()
$dur = ($xml.Event.EventData.Data | Where-Object Name -eq 'CapDurationInSeconds').'#text'
if ($dur -ge 3600) { continue }
Only what survives all three filters counts as an episode. On my machine: 0. The verdict also changed from a statement ("your cooling is the problem") to a hedged one ("very probably") with a simple test the user can do themselves.
A PowerShell trap I hit on the way
In the same session I broke the translation table. My loop used a variable named $t for the event time. The table of translated strings was called $T. PowerShell variable names are case-insensitive, so $t silently overwrote $T and every string came back empty. If you use single-letter variables in a script that already has one in capitals, rename one of them.
What I have not proved
I recalibrated this on one machine, mine. The three filters remove the false positive I could see, but I do not yet know the false-positive rate across other laptops and desktops. A 10-PC field test is next. If you have Event 37 data from machines with a real cooling fault, I would like to see how the filters behave.
The tool
This is part of Nyctale, a Windows PC diagnostic tool that tries to name the actual cause of a slow or hot PC, and says "no software will fix this" when that is the honest answer. The fixed rule shipped in version 1.0.3. The quick diagnostic is free.
Have you used Event 37 for anything? Did you find other signals that separate real thermal throttling from firmware power limits?
Top comments (1)
event id 37 just means the OS throttled the core, you have to correlate it with actual temperature readings or power‑limit changes before blaming the cooler