ERROR_NOT_OWNER (0x120): Release Mutex Not Owned Fix
This error means your program tried to release a mutex it doesn't own. It's a threading bug. Here's how to find and fix the culprit.
Fix 1: The Thread Doesn't Actually Own the Mutex
This is the most common cause. The thread calling ReleaseMutex was never the one that called WaitForSingleObject (or WaitForMultipleObjects) on that mutex. I've seen this trip up devs who pass a mutex handle between threads without checking ownership. A mutex in Windows is owned by the thread that acquired it — no other thread can release it, period.
How to verify ownership
- In C++, use
GetLastError()afterReleaseMutexto catch this error. - In .NET, wrap
Mutex.ReleaseMutex()in a try-catch forAbandonedMutexExceptionorApplicationException— but the error shows up as anUnauthorizedAccessExceptionon some frameworks. - If using Win32 directly, check that the thread ID calling
ReleaseMutexmatches the thread ID that acquired it. You can store the owning thread ID when you first acquire the mutex.
I once spent two hours debugging a server process where a background thread was releasing a mutex that the main thread held. The fix: make sure only the acquiring thread calls release, and if you need cross-thread signaling, use an event or semaphore instead.
Fix 2: You're Releasing the Same Mutex Twice
This one's sneaky. Your code calls ReleaseMutex once correctly, then accidentally calls it again — maybe in a cleanup routine or error handler. The second call fails with ERROR_NOT_OWNER because after the first release, the mutex is no longer owned by any thread. This is especially common in C++ destructors or .NET Dispose methods that aren't guarded by a check.
How to prevent double release
- Set the mutex handle to
NULLorINVALID_HANDLE_VALUEafter releasing it. - In .NET, use a
bool _isOwnedflag. Only callReleaseMutex()if_isOwnedis true, then set it false. - Check the return value of
ReleaseMutex— if it returnsFALSEandGetLastError()isERROR_NOT_OWNER, log the call stack. I've seen this happen in complex callback chains where multiple paths call release.
Here's a C++ snippet that avoids double release:
if (hMutex != NULL) {
if (!ReleaseMutex(hMutex)) {
DWORD err = GetLastError();
if (err == ERROR_NOT_OWNER) {
// Log: mutex already released or not owned
}
}
hMutex = NULL; // critical: prevent reuse
}
Fix 3: Mutex Abandonment — The Thread Exited Without Releasing
Less common but nasty: a thread acquires a mutex and then crashes or terminates without releasing it. Windows marks the mutex as abandoned. The next thread that waits on it gets WAIT_ABANDONED — but if that new thread tries to release the mutex, it gets ERROR_NOT_OWNER. Why? Because the mutex's ownership was broken by the crash. The new thread can now hold the mutex, but the system expects you to handle the abandoned state explicitly.
The fix
- Check the return value of
WaitForSingleObject. If it'sWAIT_ABANDONED, don't callReleaseMutexon that handle — instead, close it and create a new mutex, or reset your shared state. - In .NET, catch
AbandonedMutexExceptionand decide whether to continue or abort the operation. - Use structured exception handling (SEH) or try-finally blocks to guarantee release under normal conditions, but accept that crashes will orphan the mutex.
I've seen this most often in Windows services that spawn worker threads. A worker throws an unhandled exception, the mutex is abandoned, and the next request fails with ERROR_NOT_OWNER. The fix: wrap thread work in a top-level try-catch that releases the mutex before exiting.
Quick-Reference Summary Table
| Cause | Symptom | Fix |
|---|---|---|
| Thread doesn't own mutex | Release called by wrong thread | Store owning thread ID, validate before release |
| Double release | Two ReleaseMutex calls for one acquire | Null or flag the handle after release |
| Mutex abandonment | Thread crashed while owning mutex | Check for WAIT_ABANDONED, handle separately |
Was this solution helpful?