File-New-Project/EarTrumpet · error · ZombieProcessException
Process is a zombie: {processId}
Error message
Process is a zombie: {processId} What it means
DesktopAppInfo constructs display info for a Win32 audio process. It first tries Kernel32.OpenProcess; if that returns a zero handle it falls back to an NtQuerySystemInformation scan to recover the image name. Only when BOTH fail (cannot open the process AND cannot find the PID in the system process list) does it throw ZombieProcessException. The process is effectively gone or inaccessible, so no metadata can be gathered.
Source
Thrown at EarTrumpet/DataModel/AppInformation/Internal/DesktopAppInfo.cs:63
}
finally
{
Kernel32.CloseHandle(handle);
}
}
else
{
trackProcess = false;
if (TryGetExecutableNameViaNtByPid(processId, out var imageName))
{
ExeName = Path.GetFileNameWithoutExtension(imageName);
SmallLogoPath = imageName;
PackageInstallPath = imageName;
}
else
{
throw new ZombieProcessException(processId);
}
}
// Display Name priority:
// - AppsFolder
// - A window caption
// - Exe Name
try
{
var appResolver = (IApplicationResolver)new ApplicationResolver();
appResolver.GetAppIDForProcess((uint)processId, out string appId, out _, out _, out _);
Marshal.ReleaseComObject(appResolver);
var shellItem = Shell32.SHCreateItemInKnownFolder(FolderIds.AppsFolder, Shell32.KF_FLAG_DONT_VERIFY, appId, typeof(IShellItem2).GUID);
DisplayName = shellItem.GetString(ref PropertyKeys.PKEY_ItemNameDisplay);
}
catch (COMException ex)
View on GitHub (pinned to aa894e51c2)
Solutions
- Catch ZombieProcessException at the call site that constructs DesktopAppInfo and skip / hide the corresponding session rather than crashing.
- Before constructing, liveness-check the PID with Process.GetProcessById (catch ArgumentException) or WaitForSingleObject on a opened handle.
- Treat the exception as transient only if you can confirm the PID is still allocated to a live process; otherwise discard the stale session.
- If access-denied is the cause, ensure EarTrumpet runs with sufficient privilege or ignore inaccessible processes.
Example fix
// before
var info = new DesktopAppInfo(pid, trackProcess: true);
// after
try { info = new DesktopAppInfo(pid, trackProcess: true); }
catch (ZombieProcessException) { /* session's process is dead; drop it */ return; } Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check process liveness before constructing DesktopAppInfo
bool Alive(int pid)
{
try { using (var p = Process.GetProcessById(pid)) { return !p.HasExited; } }
catch (ArgumentException) { return false; }
catch (InvalidOperationException) { return false; }
} Try / catch
try { info = new DesktopAppInfo(pid, trackProcess); } catch (ZombieProcessException ex) { Trace.WriteLine($"Dead process {pid}: {ex}"); info = null; } Prevention
- Always wrap DesktopAppInfo construction in try/catch(ZombieProcessException) at the session-display boundary.
- Drop the session gracefully instead of crashing the flyout.
- Do not retry the same dead PID; it will keep failing.
When it happens
Trigger: An audio session still references a processId whose process has terminated (or that the current user cannot open) by the time DesktopAppInfo is constructed: OpenProcess returns IntPtr.Zero and TryGetExecutableNameViaNtByPid finds no matching UniqueProcessId.
Common situations: A short-lived audio-playing process exits while the EarTrumpet flyout is open; the audio host crashed; access denied opening a higher-privilege process; PID reused between session creation and info lookup.
Related errors
AI-assisted analysis of File-New-Project/EarTrumpet@aa894e51c2 (2026-08-13).
Data as JSON: /api/errors/11068227775b2c05.
Report an issue: GitHub.