schtasks says the task is disabled, Get-ScheduledTask says Ready, seconds apart

6 min read WindowsTask SchedulerAntivirusAutomation

AVG Gaming Mode mass-disables every Windows scheduled task and re-enables them minutes later. The trigger is not a game: browsers sit in the Gaming Mode app list, so any headless-browser automation does it to you.

TL;DR · THE FIX

AVG Gaming Mode disables EVERY scheduled task on the machine and re-enables them minutes later (event 141 on the AVAST Gaming mode Task Scheduler recovery task, paired with event 142 on all of yours). The trigger is its app list, which ships with Edge, Brave and Discord in it, so headless-browser automation launches it. schtasks /run then refuses with "it is disabled" while Get-ScheduledTask reports Ready seconds later. Real fix: turn off the Do Not Disturb rule "Pause Windows Updates", which is implemented as disabling every task on the machine.

The symptom

schtasks /run /tn "MorningDigest"
# ERROR: The task cannot be run because the task is disabled.

Get-ScheduledTask -TaskName "MorningDigest" | Select-Object State
# State
# -----
#  Ready

Those two commands ran about four seconds apart. One says disabled, one says ready, and both were telling the truth about the moment they asked.

The task’s XML had no <Enabled>false</Enabled> anywhere in it. Nothing in my code disables tasks and nothing had been deployed. Over the course of a day the same thing hit several unrelated tasks, including ones I had not touched in months.

What I tried first

I recreated the task, which is the thing you should not do. It worked briefly and then stopped again, which taught me only that whatever was happening was not stored in the task definition. I checked group policy in case something was disabling tasks by policy, and found nothing. I checked whether the task was being disabled by its own failure count, which is not a real Windows behaviour; by then I was reaching.

What I should have done first, and eventually did, was stop looking at the task and look at the machine. Instead of “why is this task disabled”, ask “how many tasks changed state today”:

Get-WinEvent -LogName Microsoft-Windows-TaskScheduler/Operational -MaxEvents 2000 |
  Where-Object { $_.Id -in 140, 141, 142 } |
  Group-Object Id |
  Select-Object Name, Count

Event 142 came back on every scheduled task on the machine, Microsoft’s own maintenance tasks included, in cycles across the whole day. Two dozen tasks do not develop the same problem at the same moment nine times over, so the cause had to be external.

What was happening

AVG Gaming Mode exists to stop background work interrupting a full-screen application. One of its Do Not Disturb rules is called “Pause Windows Updates”, and it is implemented by disabling every scheduled task on the machine, not only Windows Update’s, which is why OneDrive, Edge Update and Brave Update get swept up alongside yours. The paired events make it unmistakable once you know to look: event 141 on AVAST Software\Gaming mode Task Scheduler recovery is Gaming Mode toggling, and event 142 on all of your tasks at the same timestamp is every one of them going down together.

That explains each confusing part of the symptom. The state oscillates, so two commands seconds apart can disagree and both be right. The task XML has nothing in it because the disable is applied to the task’s live registration rather than written into the definition file, which is why inspecting the XML returns nothing and makes you doubt the error message.

Two things I got wrong in the first version of this post

The vendor is AVG. Only AVG is installed on the machine that produced this evidence, and the confusion is built in: AVG and Avast are one company shipping one engine, and the offending task is registered under \AVAST Software\Gaming mode Task Scheduler recovery either way. An Avast-branded string in your logs does not mean Avast is installed. Check C:\Program Files and your running services before you name the product.

The trigger is not a game, which is why the outage kept coming back after I thought I understood it. C:\ProgramData\AVG\Antivirus\gaming_mode\games.json is the Gaming Mode app list. On this machine eleven entries were enabled and six of them are not games: stable Edge, Edge Dev, Brave, WSL\msrdc.exe, Microsoft.Edge.GameAssist, and Discord.

Stable Edge is the one that bites, because every verification path I have drives headless Edge: deploy checks, the uploaders, playwright-core via executablePath, Lighthouse via CHROME_PATH. Each of those runs looks to the antivirus like a game launching, so it enters Gaming Mode and every scheduled task on the machine goes down with it. The outage was self-inflicted, and that explains a recurrence pattern I had been reading as random: it correlated with my busiest days because my own tooling caused it. Over three days I measured 4731 disable events against 229 enable events, cycling roughly every five minutes.

If you drive a browser headlessly for anything, from scraping to CI to deploy verification, you are inside this blast radius, and the word “gaming” is what will keep you from suspecting it.

The fix

Turn off the Do Not Disturb rule “Pause Windows Updates”. That single rule is the mass disable; it writes GameRule_PauseAllUpdateTasks_Enabled and ships enabled by default.

I got the location wrong twice. It is not under Settings > General, and not on the Performance page either, which holds the Do Not Disturb module while the individual toggles are registered as a separate searchable settings section. The route that does not depend on guessing the section tree:

AVG > Menu > Settings > search “disturb”

Taking the browsers out of games.json would also work, but every programmatic route to it is closed. The file is protected by AVG self-defense and writing returns Errno 13 Permission denied even though the ACL looks writable, Disable-ScheduledTask on the recovery task returns Access denied unelevated, and the Gaming Mode registry key needs elevation. Do it in the UI or not at all.

Then verify it stopped rather than trusting the toggle. Before: 396 disable events in 45 minutes, arriving every one to five minutes. After: zero across the following twelve minutes, with every task reading Ready.

Enable-ScheduledTask on your own tasks does work unelevated and recovers the current day, but the next headless-browser run restarts the cycle.

The detection query

Worth keeping after the fix, because it turns “my task is disabled” into a five-second answer:

$ev = Get-WinEvent -LogName Microsoft-Windows-TaskScheduler/Operational -MaxEvents 2000 -ErrorAction SilentlyContinue |
      Where-Object { $_.Id -eq 142 -and $_.TimeCreated -gt (Get-Date).AddDays(-1) }
$byTask = $ev | Group-Object { $_.Properties[0].Value }
"{0} disable events across {1} distinct tasks in 24h" -f $ev.Count, $byTask.Count
if ($byTask.Count -gt 3) { "MASS DISABLE: this is external, not your task" }

The count across distinct tasks is the signal. A genuine disable hits one task. Three or more unrelated tasks disabled together, especially with Microsoft’s own in the list, means something on the machine is doing it in bulk and your task is a bystander.

So never recreate or re-enable a task on the strength of one Status: Disabled read. Read it twice, a minute apart, and check the 142 count first. Recreating a task in response to an oscillating external toggle loses whatever history and settings the original had, and it feels like it worked only because the oscillation flips back on its own.

One more thing to tell apart: Last Result: -2147020576 (0x80070420) with event 153 is a missed trigger with the PC asleep, a different problem entirely. Read the log rather than the status field.

The lesson

When one thing looks broken, count how many things look broken. A single-task investigation cannot distinguish a task-level cause from a machine-level one, and it will generate plausible task-level theories forever because there is always another setting to check.

If two reads disagree, the value may be oscillating. My instinct was that schtasks and Get-ScheduledTask were reporting different fields, which was a reasonable guess and completely wrong: they agreed perfectly and the machine changed underneath them. When something else is toggling a state, one sample tells you little. Sample twice, or report the transition count instead of the state.

And the one that cost the most time: a feature’s name describes who it was built for and says nothing about what it touches. “Gaming Mode” reads as irrelevant on a machine with no games, so I ruled it out early on the name alone. It was an allow-list of executables populated by someone else’s heuristic, and that heuristic had put my browser in it.

Related fixes

Discussion

Powered by GitHub. Sign in to leave a comment.