0X00002776

WSAENOMORE 0x00002776: WSALookupServiceNext Fix Guide

WSAENOMORE means your WSALookupServiceNext loop never stopped. It's almost always a missing buffer-length reset, not a broken network.

You called WSALookupServiceNext in a loop, and now it's spitting back WSAENOMORE (0x00002776) — also known as WSA_E_NO_MORE on newer SDKs. I know how irritating this one is, because the error sounds like a failure when it's actually the API telling you it's done. Nine times out of ten, the fix isn't network-related at all. Your enumeration loop just doesn't know when to quit.

Quick context so the rest makes sense: WSALookupServiceBegin starts a query against a namespace provider (Bluetooth, SSDP, Active Directory, whatever). You then call WSALookupServiceNext repeatedly to walk the results. When there are no more results, the API returns WSAENOMORE. That's a signal, not an error. The bug is that coders treat it as a fatal condition instead of a clean exit.

Cause 1: You're not breaking out of the loop on WSAENOMORE

This is the big one. I've seen this exact bug in Bluetooth device scanners, mDNS discovery tools, and SSDP upnp browsers written by otherwise solid engineers. The loop looks like this:

while (true) {
    dwSize = sizeof(buffer);
    ret = WSALookupServiceNext(hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pResults);
    if (ret == SOCKET_ERROR) {
        int err = WSAGetLastError();
        // BUG: only checking for WSA_E_NO_MORE here sometimes,
        // or not checking at all, and printing a scary error
        break;
    }
    // process pResults...
}

The correct pattern is dead simple. Check for WSAENOMORE (or WSA_E_NO_MORE depending on your headers) and break cleanly without logging it as a failure:

while (true) {
    dwSize = sizeof(buffer);
    ret = WSALookupServiceNext(hLookup, flags, &dwSize, pResults);
    if (ret == SOCKET_ERROR) {
        int err = WSAGetLastError();
        if (err == WSAENOMORE || err == WSA_E_NO_MORE) {
            break;  // normal exit — we enumerated everything
        }
        // real error, handle it
        break;
    }
    // process pResults...
}
WSALookupServiceEnd(hLookup);

Note the two constants. On Windows SDK 6 and earlier, you get WSAENOMORE (10102). On newer SDKs, Microsoft renamed it to WSA_E_NO_MORE with the same numeric value. If you're compiling against a modern SDK, only the second symbol is defined, and your old code that checked WSAENOMORE won't even build. Check both if you support a wide version range.

The real-world trigger: a device discovery feature in a pairing utility. User hits "Scan for devices," the scan finds three headsets, then the fourth call to WSALookupServiceNext returns WSAENOMORE. Your code logs "Error 10102" and pops a red banner. The user thinks their Bluetooth radio is broken. It isn't. You just need to stop asking.

Cause 2: Buffer size isn't being reset between iterations

This one is sneakier and it trips up experienced devs. On every call to WSALookupServiceNext, you must set dwSize to the size of your buffer before the call. The API overwrites it with the actual bytes written. If you forget to reset it, the second iteration passes a shrunken size, the call fails with WSAEFAULT, and you interpret that as "no more results."

Here's the pattern that works:

WSAQUERYSET qs = {0};
DWORD dwSize = sizeof(qs);

while (true) {
    dwSize = sizeof(qs);  // RESET every time
    memset(&qs, 0, sizeof(qs));
    qs.dwSize = sizeof(qs);
    int ret = WSALookupServiceNext(hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, &qs);
    if (ret == SOCKET_ERROR) {
        int err = WSAGetLastError();
        if (err == WSAENOMORE || err == WSA_E_NO_MORE) break;
        // handle real error
        break;
    }
    // use qs...
}

If you're using WSAQUERYSET larger than the default (say, with a big lpBlob for service-specific data), allocate it on the heap with malloc and track the size separately. Stack buffers over 64KB cause their own problems and won't help you here.

Also make sure qs.dwSize is set to the struct size before each call. Some providers check it.

Cause 3: You're calling WSALookupServiceNext after WSALookupServiceEnd

This one produces WSAENOMORE in a weird way: the handle is stale, the provider has torn down its result set, and the call returns "no more results" because to the OS, there's nothing left to iterate. But your code thinks it's mid-scan.

Typical trigger: a multi-threaded app where thread A closes the scan on a cancel button, and thread B is still running its enumeration loop. Thread B gets WSAENOMORE on the next iteration, doesn't know the handle is invalid, and keeps looping or crashes on a double-free when it eventually calls WSALookupServiceEnd a second time.

Fix: use a reference count or a proper cancellation flag. Don't call WSALookupServiceEnd until all enumeration threads have exited their loops. And always check the return value of WSALookupServiceEnd — if it fails with WSAEINVAL, you're double-ending.

// On the cancel path
InterlockedExchange(&g_cancelRequested, 1);
// wait for enumeration thread to signal done
WaitForSingleObject(g_enumThread, INFINITE);
WSALookupServiceEnd(hLookup);

Some providers (Bluetooth especially, on Windows 10 and 11) will tear down the handle if the underlying radio resets. That's WSAENOMORE appearing out of nowhere in the middle of a scan. Defensive coding: treat WSAENOMORE as a clean exit even if you expected more results, and surface a "scan incomplete" state to the UI.

What about WSA_E_NO_MORE vs WSAENOMORE?

Same value (10102 / 0x2776), different symbol names. Microsoft deprecated WSAENOMORE in favor of WSA_E_NO_MORE around the Windows SDK 6.1 era. If you're getting a compile error about undefined WSAENOMORE, use WSA_E_NO_MORE. If you're getting an undefined WSA_E_NO_MORE, you're on an old SDK — update or use the raw value 10102. Yes, hardcoding is ugly. It also works.

Is this ever a real network problem?

Almost never. If a namespace provider itself fails, you get WSASERVICE_NOT_FOUND (10108) or WSATYPE_NOT_FOUND (10109), not WSAENOMORE. If a firewall blocks discovery, you typically get a timeout or an empty result set that still returns WSAENOMORE on the first iteration. So no — don't chase your network stack. Chase your loop.

Quick reference

CauseSymptomFix
Missing break on WSAENOMORELoop never exits, or logs error 10102 as failureCompare WSAGetLastError() against WSAENOMORE / WSA_E_NO_MORE and break
dwSize not reset per callSecond iteration fails with WSAEFAULT, treated as end-of-resultsSet dwSize = sizeof(buffer) at the top of each loop iteration
Use after WSALookupServiceEndRandom WSAENOMORE mid-scan, possible double-freeSerialize end vs. enum; check cancel flag; don't end twice
Wrong symbol nameCompile error: WSAENOMORE undefinedUse WSA_E_NO_MORE on modern SDKs

If you fix only one thing, fix the break. That's the bug 90% of the time.

Related Errors in Network & Connectivity
0XC0262000 0XC0262000: Exclusive mode ownership fix for unmanaged primary allocation 0X000026B1 Fix DNS_ERROR_DP_NOT_AVAILABLE (0X000026B1) in AD partitions Router DHCP Not Assigning IP Addresses — 3 Fixes That Actually Work 0X000004D2 ERROR_PORT_UNREACHABLE (0X000004D2) — Fix in 3 Steps

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.