I have a logon task that launches a Node server through a hidden wscript/VBScript wrapper. During updates, my script used to stop the scheduled task, copy in the new files, and start the task again. The problem was that Stop-ScheduledTask changed the task state to Ready but left node.exe running and holding the server port. The replacement process then failed with EADDRINUSE, while health checks continued passing against the old version.
I now stop the task and then locate node.exe by its command line, matching the exact server.js path, rather than stopping every Node process on the machine. I also wait for the process and port to disappear before starting the replacement. Afterward, I check which process owns the listening port and verify that it is running the new copy.
The command-line check needs to account for CommandLine being null when inspecting processes owned by another user without elevation. I also discovered that native PowerShell commands need explicit $LASTEXITCODE checks, and that robocopy codes from 1 through 7 indicate success.
Is this expected behavior when a scheduled task launches a script host that starts several child processes? The task becomes Ready, but the descendants continue running.
3 Answers
Yes. Task Scheduler generally controls the process it directly launched, not every descendant created by that process. In this case the chain is wscript, then the script and command shell, then node.exe. Stopping the task ends the top-level host, but node can remain alive and keep its port. Stopping the specific server process directly, waiting for it to exit, and then checking the port is a much safer update sequence.
The process-management fix makes sense, but the wrapper is becoming a liability. VBScript and wscript are deprecated and may not be available by default in the future. A service manager or a real Windows service would be cleaner for a normal background server, and it would give you proper stop and restart semantics.
I agree that the wrapper should eventually be replaced. A regular service is awkward here because the Node process opens desktop applications that must appear in the logged-in user’s session; services run separately and cannot directly provide that interactive desktop behavior. A logon task is still the best fit for this particular use case, but it should not be trusted to terminate the entire process tree.
Be careful with the port check. A listening port can have more than one relevant process or another component may bind it before your server does. Checking only the first result can produce a false positive. Match the owning PID back to its full command line, confirm the expected executable and script path, and ideally bind or test on localhost if that is the intended endpoint.
Good catch. The first-listener assumption is not sufficient when another local component can share or claim the port. The rollback check now needs to identify the expected Node process explicitly rather than trusting that anything answering on the port is the new server.

That matches what I measured: the task reported Ready while node.exe was still alive. I’m treating the task only as a launcher now and handling shutdown and verification separately.