cheat sheet

taskkill

End one or more running Windows processes by PID or image name from the command prompt, with options for force termination, process tree killing, filtering, and remote targets.

taskkill — Terminate Processes

What it is

taskkill is a built-in Windows command that terminates one or more running processes, identified by PID (/PID) or image name (/IM). By default it sends a graceful shutdown signal (WM_CLOSE) and waits for the process to exit; the /F flag forces immediate termination (TerminateProcess). The /T flag kills the entire process tree rooted at the target — essential for shutting down applications that spawn child processes. In scripts, taskkill is almost always paired with tasklist to first confirm the process is running and obtain its PID.

Availability

taskkill ships as C:\Windows\System32\taskkill.exe on Windows XP and later. PowerShell equivalent: Stop-Process.

cmd
taskkill /?

Output:

css
TASKKILL [/S system [/U username [/P [password]]]]
         { [/FI filter] [/PID processid | /IM imagename] } [/T] [/F]

Syntax

cmd
taskkill [/S host] [/U user] [/P pass] { [/FI filter] [/PID pid | /IM image] } [/T] [/F]

Output: (success or error message per process)

Essential options

SwitchMeaning
/PID pidTarget process by numeric PID
/IM imageTarget process by image name (*.exe); wildcards allowed
/FForce-terminate (TerminateProcess) — no graceful shutdown
/TKill the process and all child processes it spawned
/FI filterApply a filter (same syntax as tasklist /FI)
/S hostRemote machine
/U domain\userCredentials for remote kill
/P passwordPassword for /U

Kill by PID

/PID targets a specific process by its unique numeric ID — safer than killing by name when multiple instances of an executable are running.

cmd
taskkill /PID 8420

Output:

vbnet
SUCCESS: Sent termination signal to the process with PID 8420.
cmd
rem Multiple PIDs in one command
taskkill /PID 8420 /PID 8421 /PID 8422

Output:

vbnet
SUCCESS: Sent termination signal to the process with PID 8420.
SUCCESS: Sent termination signal to the process with PID 8421.
SUCCESS: Sent termination signal to the process with PID 8422.

Kill by image name

/IM kills all processes whose executable name matches. Wildcards (*) are supported — * alone matches everything, which terminates all user processes.

cmd
taskkill /IM notepad.exe

Output:

vbnet
SUCCESS: Sent termination signal to the process "notepad.exe" with PID 8420.
cmd
rem Kill all chrome instances
taskkill /IM chrome.exe

Output:

vbnet
SUCCESS: Sent termination signal to the process "chrome.exe" with PID 6124.
SUCCESS: Sent termination signal to the process "chrome.exe" with PID 6130.

Force termination (/F)

Without /F, taskkill sends a graceful close message; if the process ignores it or is frozen, the call returns success but the process stays running. /F calls TerminateProcess immediately — no cleanup, no save dialogs.

cmd
taskkill /F /IM myapp.exe

Output:

vbnet
SUCCESS: The process "myapp.exe" with PID 7890 has been terminated.
cmd
taskkill /F /PID 8420

Output:

vbnet
SUCCESS: The process with PID 8420 has been terminated.

Kill process tree (/T)

/T walks the process tree and terminates the target together with all descendants. This is essential for applications like build tools or test runners that spawn child processes — without /T, orphan children continue running.

cmd
taskkill /T /F /PID 5432

Output:

vbnet
SUCCESS: The process with PID 5432 has been terminated.
SUCCESS: The process with PID 5433 has been terminated.
SUCCESS: The process with PID 5434 has been terminated.
cmd
rem Kill a node.js server and all its worker forks
taskkill /T /F /IM node.exe

Output:

vbnet
SUCCESS: The process "node.exe" with PID 5432 has been terminated.
SUCCESS: The process "node.exe" with PID 5435 has been terminated.

Filter-based termination (/FI)

/FI accepts the same filter syntax as tasklist /FI, letting you target processes by memory usage, status, session, username, or other attributes.

cmd
rem Kill all non-responding processes
taskkill /F /FI "STATUS eq Not Responding"

Output:

vbnet
SUCCESS: The process "myapp.exe" with PID 7890 has been terminated.
cmd
rem Kill all processes using more than 500 MB
taskkill /F /FI "MEMUSAGE gt 512000"

Output:

vbnet
SUCCESS: The process "chrome.exe" with PID 6124 has been terminated.
cmd
rem Kill all processes in a specific session
taskkill /F /FI "SESSION eq 2"

Output:

vbnet
SUCCESS: The process "explorer.exe" with PID 4200 has been terminated.
...

Remote process termination (/S)

/S targets a remote machine. Requires network access and appropriate permissions on the target.

cmd
taskkill /S myhost /U DOMAIN\alicedev /F /IM myapp.exe

Output:

vbnet
Password: (prompted)
SUCCESS: The process "myapp.exe" with PID 7890 has been terminated.

Combining tasklist and taskkill

The canonical pattern for conditional termination: use tasklist to find the PID, then pass it to taskkill.

cmd
@echo off
for /f "tokens=2" %%p in ('tasklist /fi "IMAGENAME eq myapp.exe" /fo csv /nh 2^>NUL') do (
    set PID=%%~p
)
if defined PID (
    taskkill /F /PID %PID%
    echo Killed PID %PID%.
) else (
    echo myapp.exe not running.
)

Output:

yaml
Killed PID 7890.

Common pitfalls

  1. Graceful kill may silently fail — without /F, taskkill returns success even if the process ignores the close signal; always add /F in scripts.
  2. /IM * kills everything — using a bare wildcard terminates all user-space processes including explorer.exe; be specific.
  3. Cannot kill protected or system processesSystem, smss.exe, and other early-boot processes are protected; even with elevation they cannot be terminated with taskkill.
  4. /T without /F may leave children — if children ignore the graceful signal, add /F to force-terminate the whole tree.
  5. "No tasks running" exit code is 128taskkill /IM notrunning.exe exits with code 128 and prints an informational message, not an error; check errorlevel 128 if you need to detect this.
  6. Session vs PID targeting for multi-instance apps — for apps like chrome.exe running across sessions, combine /FI "SESSION eq N" with /IM to avoid killing processes in other user sessions.

Real-world recipes

Restart a service executable cleanly

cmd
@echo off
taskkill /F /IM myservice.exe
timeout /T 2 /NOBREAK > NUL
start "" "C:\Services\myservice.exe"
echo myservice restarted.

Output:

vbnet
SUCCESS: The process "myservice.exe" with PID 4567 has been terminated.
myservice restarted.

Kill all non-responding processes in a maintenance script

cmd
@echo off
echo Checking for non-responding processes...
taskkill /F /FI "STATUS eq Not Responding" > NUL
echo Done.

Output:

rust
Checking for non-responding processes...
Done.

Conditionally kill a process only if it is running

cmd
@echo off
tasklist /fi "IMAGENAME eq notepad.exe" 2>NUL | findstr "notepad.exe" >NUL
if not errorlevel 1 (
    taskkill /F /IM notepad.exe
    echo Notepad closed.
) else (
    echo Notepad was not running.
)

Output:

vbnet
SUCCESS: The process "notepad.exe" with PID 8420 has been terminated.
Notepad closed.

Kill a process tree by name for a test teardown

cmd
taskkill /T /F /IM pytest.exe > NUL 2>&1
taskkill /T /F /IM python.exe > NUL 2>&1
echo Test processes cleaned up.

Output:

code
Test processes cleaned up.

Graceful vs forced: what actually happens

taskkill without /F posts WM_CLOSE to every top-level window owned by the target thread; the application's message loop then has a chance to prompt for unsaved data and exit cleanly. /F skips all of that and calls TerminateProcess() — the kernel rips the process's address space out from under it with no atexit, no destructors, and no flushing of file buffers. Understanding which to use is the single most important taskkill decision.

Signal sentAPI usedWhat the process seesCleanup runs?
(no flag)PostMessage(WM_CLOSE) to GUI threadsA "please close" hintYes (if it cooperates)
/FTerminateProcess()Nothing — instant deathNo
/F /TTerminateJobObject() plus per-PID TerminateProcess()NothingNo
Ctrl+C (GenerateConsoleCtrlEvent)Console signalSetConsoleCtrlHandler firesYes (in handler)

Practical implications:

  • A console app with no GUI window will ignore the bare taskkill — it never receives WM_CLOSE. Always use /F for console apps.
  • A GUI app with an unsaved-changes prompt will show its "Save changes?" dialog and stay running until the user interacts. Use /F for unattended scripts.
  • A process holding a file lock or pipe will leave that resource in a half-closed state after /F. Other processes may need to retry I/O for a few seconds after the kill.
  • Database engines (SQL Server, PostgreSQL, etc.) treat /F as a crash and may run recovery on next startup. Prefer sc stop or the engine's own shutdown path; only fall back to taskkill /F when those hang.
cmd
rem Demonstrating graceful failure on a console app
taskkill /IM pythonw.exe

Output:

vbnet
SUCCESS: Sent termination signal to the process "pythonw.exe" with PID 5432.

The "SUCCESS" message only confirms that the signal was sent — the process may still be alive. Verify with tasklist:

cmd
tasklist /fi "IMAGENAME eq pythonw.exe"

Output:

arduino
Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
pythonw.exe                   5432 Console                    1     42,332 K

Add /F to actually terminate it:

cmd
taskkill /F /IM pythonw.exe

Output:

vbnet
SUCCESS: The process "pythonw.exe" with PID 5432 has been terminated.

Note the message wording difference: "Sent termination signal" (graceful) vs "has been terminated" (forced). When you read script logs, that wording is the only reliable indicator of which path was taken.

Exit codes in detail

taskkill is more careful than tasklist about distinguishing outcomes, which makes it scriptable in a way tasklist is not.

Exit codeCondition
0At least one process was killed (or signalled) successfully
128No matching processes — "ERROR: The process … not found."
255Access denied — caller lacks the required privilege
1Bad parameters or filter syntax
cmd
taskkill /IM doesnotexist.exe
echo Exit: %ERRORLEVEL%

Output:

vbnet
ERROR: The process "doesnotexist.exe" not found.
Exit: 128
cmd
rem Permission denied on a protected process
taskkill /F /IM csrss.exe
echo Exit: %ERRORLEVEL%

Output:

vbnet
ERROR: The process with PID 680 could not be terminated.
Reason: Access is denied.
Exit: 255
cmd
rem Defensive script that swallows "not found" but propagates real errors
taskkill /F /IM myapp.exe
if errorlevel 255 ( echo Hard error & exit /b 1 )
if errorlevel 128 ( echo Already gone & exit /b 0 )
echo Killed cleanly

Output:

code
Killed cleanly

PowerShell equivalent — Stop-Process

Stop-Process (alias kill/spps) is the modern replacement and what new PowerShell scripts should use. It always force-terminates — there is no graceful mode — but it accepts the pipeline so you can chain it with filters and confirmation logic that taskkill cannot express.

Direct equivalents

taskkillStop-Process
taskkill /F /PID 8420Stop-Process -Id 8420 -Force
taskkill /F /IM notepad.exeStop-Process -Name notepad -Force
taskkill /F /IM "chrome.exe" (multiple)Stop-Process -Name chrome -Force
taskkill /S host /F /PID 8420Invoke-Command -ComputerName host { Stop-Process -Id 8420 -Force }

Pipeline patterns

powershell
Get-Process notepad -ErrorAction SilentlyContinue | Stop-Process -Force

Output: (no output on success)

powershell
Get-Process | Where-Object { $_.WorkingSet64 -gt 500MB -and $_.Name -ne 'sqlservr' } |
    Stop-Process -Force -WhatIf

Output:

csharp
What if: Performing the operation "Stop-Process" on target "chrome (6124)".
What if: Performing the operation "Stop-Process" on target "code (8732)".

-WhatIf is the safety net taskkill doesn't have — preview the kill list before committing. -Confirm prompts per process.

Stop-Process limitations vs taskkill

  • No graceful mode. Stop-Process is always force; if you need WM_CLOSE semantics use (Get-Process notepad).CloseMainWindow() first and only call Stop-Process as a fallback.
  • No /T flag — to kill a tree, enumerate children manually:
powershell
function Stop-ProcessTree {
    param([int]$Id)
    Get-CimInstance Win32_Process -Filter "ParentProcessId=$Id" |
        ForEach-Object { Stop-ProcessTree -Id $_.ProcessId }
    Stop-Process -Id $Id -Force -ErrorAction SilentlyContinue
}
Stop-ProcessTree -Id 5432

Output: (none on success — the tree under PID 5432 is gone)

Graceful close in PowerShell

powershell
$p = Get-Process notepad -ErrorAction SilentlyContinue
if ($p) {
    $p | ForEach-Object { $_.CloseMainWindow() | Out-Null }
    Start-Sleep -Seconds 3
    $p | Where-Object { -not $_.HasExited } | Stop-Process -Force
}

Output: (none — closes Notepad gracefully if it has no unsaved changes, force-kills otherwise)

The /T flag and job objects

/T walks the process tree at kill-time by querying NtQuerySystemInformation for each process's ParentProcessId and recursively terminating. This has two pitfalls:

  1. Reparenting. When a parent exits and its child is still running, modern Windows reparents the child to PID 4 (System) or an arbitrary ancestor — the original tree relationship is lost. /T cannot find children orphaned this way.
  2. PID reuse. Windows recycles PIDs aggressively. If a parent process exits and Windows reuses its PID before /T walks the tree, taskkill /T may kill an unrelated process that happens to be the child of the recycled PID. Race-prone in fast-spawn workloads.

The robust alternative is Job Objects — Windows' native mechanism for grouping processes. Anything you launch via start /b or PowerShell's Start-Process can be assigned to a job; killing the job kills every member atomically. Sysinternals' pskill doesn't help here either, but Stop-Process plus a Win32_Process recursion is usually safer in PowerShell than taskkill /T.

powershell
function Stop-ProcessTreeJob {
    param([int]$Id)
    $children = Get-CimInstance Win32_Process -Filter "ParentProcessId=$Id"
    foreach ($c in $children) { Stop-ProcessTreeJob -Id $c.ProcessId }
    try { Stop-Process -Id $Id -Force -ErrorAction Stop }
    catch [Microsoft.PowerShell.Commands.ProcessCommandException] { }
}

Output: (function definition — no console output)

Sysinternals PsKill

The Sysinternals pskill utility (see the Sysinternals deep-dive) is a richer kill tool worth knowing about even if you mostly stick with taskkill. It accepts PID or name, supports remote machines without DCOM, and has a useful -t (tree kill) that uses the same recursion as Stop-ProcessTree above.

cmd
pskill -accepteula 8420
pskill -t -accepteula notepad.exe
pskill \\myhost -u myhost\alicedev 8420

Output:

arduino
Process notepad.exe killed.

Why prefer pskill over taskkill:

  • Cleaner remote semantics (works over SMB without requiring Remote Registry).
  • Tree-kill is recursive via the same Win32 APIs, but pskill enumerates children at the moment of the kill rather than racing.
  • Returns clean exit codes (0/1) with a single line of output, easier to parse than taskkill's "SUCCESS: …".

Why stick with taskkill:

  • In-box; no install required.
  • Supports /FI filters like STATUS eq Not Responding which pskill lacks.
  • Standardised across Windows 10/11 even on locked-down hosts where you can't drop binaries.

Comparison: taskkill vs Stop-Process vs pskill vs wmic

CapabilitytaskkillStop-Processpskill (Sysinternals)wmic process call terminate
Built-inyesyes (PS)no — install requiredyes (deprecated)
Graceful closeyes (no /F)only via .CloseMainWindow()nono
Force kill/Falwaysalwaysalways
Tree kill/Tmanual via CIM-tno
Filter by status/FI "STATUS eq Not Responding"Where-Object Responding -eq $falsenowhere "..." clauses
Remote/SInvoke-Command\\host/node:host
Survives 2025+ Windowsyesyesyesno (24H2+)
Pipelinenoyesnono

Cross-platform: kill, killall, pkill vs taskkill

For Linux/macOS muscle memory, the closest equivalents are POSIX kill (by PID), killall (by exact name), and pkill (by name with regex). Conceptually they all take signal numbers rather than a force flag — the closest mapping:

LinuxWindows equivalentNotes
kill 1234 (SIGTERM)taskkill /PID 1234Graceful — process gets a chance to exit
kill -9 1234 (SIGKILL)taskkill /F /PID 1234Force — kernel terminates immediately
killall notepadtaskkill /IM notepad.exeBy name, all instances
killall -9 notepadtaskkill /F /IM notepad.exeForce, by name
pkill -f "node.*server"no direct equivalent — use Get-CimInstance filter on CommandLine
kill -- -PGID (kill process group)taskkill /T /F /PID PID (tree)Job Objects are closer but require setup
pkill --signal HUP (reload config)no equivalentWindows services use sc control

Windows has no SIGHUP. Services that want a "reload config" path implement it via the SCM (sc control SERVICE 128–255 for user-defined control codes) or by watching a file for changes. Don't try to emulate SIGHUP with taskkill.

Privilege requirements

Killing certain processes requires more than a standard admin token. The rules of thumb:

TargetMinimum privilege
Your own user-space processesNone
Another user's processes in your sessionAdministrator
Processes in another sessionAdministrator
Service processes (svchost, etc.)Administrator
csrss.exe, wininit.exe, services.exeCannot kill — critical, protected
Antimalware enginesOften Protected Process Light (PPL) — refuses even SYSTEM
LSASSCannot kill — would crash the system

To check whether your shell has the rights:

cmd
whoami /priv | findstr SeDebugPrivilege

Output:

rust
SeDebugPrivilege                  Debug programs                  Disabled

The privilege exists but is Disabled; admins must enable it explicitly (it's enabled automatically for the SYSTEM account). Sysinternals psexec -s opens a cmd.exe running as SYSTEM with SeDebugPrivilege already enabled — the standard escalation path when taskkill /F returns "Access denied".

Defensive scripting patterns

Idempotent kill — "stop if running, don't fail if not"

cmd
@echo off
taskkill /F /IM myapp.exe >nul 2>&1
if errorlevel 128 (
    rem 128 = not found — that's fine
    exit /b 0
)
if errorlevel 1 (
    echo Failed to kill myapp.exe ^(errorlevel %ERRORLEVEL%^)
    exit /b 1
)
echo Killed myapp.exe

Output:

code
Killed myapp.exe

Bounded retry on a stubborn process

Some processes (notably anti-malware or DRM clients) take a few seconds to actually exit after a /F. Poll with tasklist until they disappear:

cmd
@echo off
setlocal
set TRIES=0
:retry
taskkill /F /IM stubborn.exe >nul 2>&1
timeout /t 1 /nobreak >nul
tasklist /fi "IMAGENAME eq stubborn.exe" 2>nul | findstr /I "stubborn.exe" >nul
if errorlevel 1 (
    echo stubborn.exe is gone
    exit /b 0
)
set /a TRIES+=1
if %TRIES% lss 10 goto retry
echo Failed after %TRIES% attempts
exit /b 1

Output:

csharp
stubborn.exe is gone

Drain a process tree before killing

The cleanest shutdown sequence for cooperative children is: stop accepting new work, ask gracefully, wait, then escalate. PowerShell makes this composable:

powershell
$root = Get-Process myservice -ErrorAction SilentlyContinue
if ($root) {
    $root | ForEach-Object { $_.CloseMainWindow() } | Out-Null
    Start-Sleep -Seconds 10
    $still = Get-Process myservice -ErrorAction SilentlyContinue
    if ($still) {
        Write-Warning "Escalating to force-kill on $($still.Count) holdout(s)"
        $still | Stop-Process -Force
    }
}

Output:

vbnet
WARNING: Escalating to force-kill on 1 holdout(s)

Kill by command-line substring (taskkill can't, but here's how)

taskkill has no CommandLine filter. Use CIM to find PIDs by command-line substring, then pipe them in:

powershell
Get-CimInstance Win32_Process -Filter "Name='node.exe'" |
    Where-Object { $_.CommandLine -like '*--server*' } |
    ForEach-Object { Stop-Process -Id $_.ProcessId -Force }

Output: (none on success)

Common pitfalls — extended

In addition to the basics above:

  1. taskkill doesn't release the file lock immediately. After /F, the Windows file system may keep the handle table dirty for a few hundred milliseconds. Subsequent file operations against the freshly-killed process's open files should retry on ERROR_SHARING_VIOLATION.
  2. /IM * is not safer with /F — it just makes the disaster faster. The shell that ran it survives only because cmd.exe is killed asynchronously after dispatching all the children.
  3. Sessions don't isolate /F by default. A user in session 2 cannot taskkill /F processes in session 1 unless they are an administrator — but admins can. Don't assume session boundary is a security barrier.
  4. Antivirus may roll back /F. Some EDRs treat a taskkill /F against their agents as tampering and immediately relaunch the process. Use the vendor's own uninstall/stop path instead.
  5. /FI "STATUS eq Not Responding" is a snapshot. A process that's briefly busy may show "Not Responding" for one polling interval then recover. Re-check after a few seconds before killing.
  6. Windows 11 24H2/25H2 Task Manager bug — Microsoft has acknowledged that closing Task Manager on 24H2/25H2 does not always terminate Taskmgr.exe, so opening it again accumulates a fresh process each time. The clean-up is a single taskkill /F /IM Taskmgr.exe (or the PowerShell Stop-Process -Name Taskmgr -Force) — worth running before opening Task Manager to inspect memory usage on those builds.

Real-world deep recipes

Find the parent of a runaway process, kill the whole tree from the top

powershell
function Get-ProcessAncestry {
    param([int]$Id)
    $p = Get-CimInstance Win32_Process -Filter "ProcessId=$Id"
    while ($p) {
        $p | Select-Object ProcessId, Name, ParentProcessId
        if ($p.ParentProcessId -eq 0) { break }
        $p = Get-CimInstance Win32_Process -Filter "ProcessId=$($p.ParentProcessId)"
    }
}
Get-ProcessAncestry -Id 5432

Output:

yaml
ProcessId Name           ParentProcessId
--------- ----           ---------------
     5432 worker.exe                4900
     4900 launcher.exe              3456
     3456 explorer.exe              3320
     3320 userinit.exe                 0

Cleanup orchestration for a CI test runner

cmd
@echo off
rem End-of-job teardown: kill everything we might have spawned
for %%P in (pytest.exe python.exe node.exe chromedriver.exe geckodriver.exe) do (
    taskkill /F /T /IM %%P >nul 2>&1
)
rem Catch any lingering test browsers — but not the user's actual browser
taskkill /F /FI "WINDOWTITLE eq Selenium*" >nul 2>&1
echo Test harness cleaned up

Output:

code
Test harness cleaned up

Watchdog: kill any process that stays "Not Responding" for two consecutive minutes

powershell
$prevHung = @()
while ($true) {
    $curHung = Get-Process | Where-Object { -not $_.Responding } | Select-Object -ExpandProperty Id
    $dead = $prevHung | Where-Object { $_ -in $curHung }
    foreach ($id in $dead) {
        try {
            $p = Get-Process -Id $id -ErrorAction Stop
            Write-Host "Killing hung $($p.Name) ($id)"
            Stop-Process -Id $id -Force
        } catch { }
    }
    $prevHung = $curHung
    Start-Sleep -Seconds 60
}

Output:

java
Killing hung notepad (8420)

See also

Sources

learn.microsoft.com — taskkill · learn.microsoft.com — Stop-Process