0X00000190

0X00000190: Thread Already in Background Mode – Fix

Windows Errors Intermediate 👁 0 views 📅 May 29, 2026

This error means code tried to set a thread to background mode when it was already set. It's a programming bug, not a user issue. Fix it by checking the thread's current state before calling SetThreadPriority.

Cause 1: Calling SetThreadPriority with THREAD_MODE_BACKGROUND_BEGIN Twice

This is the single most common cause. What's actually happening here is that your code (or a library you're using) calls SetThreadPriority(hThread, THREAD_MODE_BACKGROUND_BEGIN) on a thread that already has background mode active. Windows returns ERROR_THREAD_MODE_ALREADY_BACKGROUND (0X00000190) because it can't set a flag that's already set.

The background mode in Windows is a toggle – you begin it, then you must end it before beginning again. The API expects a strict BEGIN/END pairing. Call BEGIN twice without an END in between, and you get 0x190.

Real-world trigger

A typical scenario: you have a worker thread that processes background tasks. You call SetThreadPriority(THREAD_MODE_BACKGROUND_BEGIN) when it starts a low-priority batch, but forget to call SetThreadPriority(THREAD_MODE_BACKGROUND_END) when the batch finishes. Next time a batch starts, that same call fails with 0x190.

The fix

Before calling BEGIN, always check the thread's current background state. The cleanest way? Use GetThreadPriority and examine the return value. If it's already THREAD_MODE_BACKGROUND_BEGIN, skip the call. But the more robust fix is to ensure every BEGIN has a matching END.

// Wrong: double BEGIN without END
SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
// ... do work ...
// forgot to call END
SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN); // BOOM: 0x190

// Right: track state
bool isBackground = false;

void startBackgroundWork() {
    if (!isBackground) {
        SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
        isBackground = true;
    }
}

void endBackgroundWork() {
    if (isBackground) {
        SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END);
        isBackground = false;
    }
}

Cause 2: Race Condition Between Threads Setting Priority

If multiple threads in your process are manipulating the priority of the same target thread, you can get 0x190 from a legitimate BEGIN/END sequence. Thread A calls BEGIN, then before Thread A can call END, Thread B also calls BEGIN on that same thread handle. Windows sees the second BEGIN while the first is still active.

The reason this is tricky: thread handles can be duplicated with DuplicateHandle and passed around. A common pattern in thread pool implementations is to let any thread adjust another thread's priority for load balancing. If two pool threads decide the same worker should go into background mode, you hit this.

Fix it with a mutex or critical section

Protect the BEGIN/END calls with a synchronization object. Use a CRITICAL_SECTION or std::mutex that's associated with the target thread's background state.

CRITICAL_SECTION g_bgLock; // initialized once

void SafeSetBackground(HANDLE hThread, bool enable) {
    EnterCriticalSection(&g_bgLock);
    if (enable) {
        SetThreadPriority(hThread, THREAD_MODE_BACKGROUND_BEGIN);
    } else {
        SetThreadPriority(hThread, THREAD_MODE_BACKGROUND_END);
    }
    LeaveCriticalSection(&g_bgLock);
}

This ensures only one thread can toggle background mode at a time. Yes, it adds a tiny overhead, but it prevents 0x190 from race conditions.

Cause 3: Calling SetThreadPriority on a Thread That's Already Exited or Is in an Invalid State

Less common but worth knowing: if the thread handle refers to a thread that has exited, SetThreadPriority returns ERROR_THREAD_MODE_ALREADY_BACKGROUND under certain conditions. This is a quirk of how Windows handles thread objects that are in a zombie state (still have a handle open but the thread is terminated).

What's happening: the kernel keeps the thread object alive until all handles are closed, but the thread's priority state gets frozen after exit. Attempting to change it triggers this specific error rather than ERROR_INVALID_HANDLE or ERROR_THREAD_NOT_IN_PROCESS.

Fix it – validate the thread before operating

Check the thread's exit code with GetExitCodeThread. If it returns STILL_ACTIVE (259), the thread is running. Anything else means it's terminated.

DWORD exitCode;
if (GetExitCodeThread(hThread, &exitCode) && exitCode != STILL_ACTIVE) {
    // Thread has exited – don't try to set priority
    return;
}
SetThreadPriority(hThread, THREAD_MODE_BACKGROUND_BEGIN);

Quick-Reference Summary

Cause Root Issue Fix
Double BEGIN Code calls BEGIN twice without END in between Track state with a boolean; ensure BEGIN/END pairs
Race condition Multiple threads call BEGIN on same thread concurrently Protect BEGIN/END with a mutex or critical section
Thread already exited Handle points to a terminated thread object Check exit code with GetExitCodeThread before calling

If you're hitting 0x190 in production code, start by auditing every call to SetThreadPriority with THREAD_MODE_BACKGROUND_BEGIN. Nine times out of ten, you'll find a missing _END call. The rest is usually a race or a stale handle. Fix those, and the error disappears.

Was this solution helpful?