Quick answer
0x00002777 (WSAECANCELLED) means someone called WSALookupServiceEnd() on a lookup handle while a WSALookupServiceNext() call on that same handle was still running — usually a second thread, a timeout handler, or a UI cancel button firing at the wrong moment.
Why this error shows up
This is one of those Winsock errors that gets blamed on the network stack when it's really an application bug. WSAECANCELLED comes out of the Winsock 2 name-resolution path (WSALookupServiceBegin / WSALookupServiceNext / WSALookupServiceEnd), which is what sits underneath getaddrinfo, Bluetooth device discovery, and a lot of mDNS/SSDP scanning on Windows.
The pattern that triggers it is almost always the same. Thread A starts a lookup and parks inside WSALookupServiceNext waiting for results. Thread B — a cancel routine, a watchdog timer, a user clicking "Stop scanning" — calls WSALookupServiceEnd on the same lookup handle. The WinSock provider sees the handle being torn down while a blocking call is still using it, aborts the pending operation, and WSALookupServiceNext returns WSAECANCELLED instead of WSA_E_NO_MORE. The app treats it as a hard error, the scan dies, and the network gets blamed.
Last quarter I had a warehouse client whose inventory app kept dropping Bluetooth scanner connections mid-shift. Their logs were full of 0x00002777. The network was fine. Their cancel button was callingWSALookupServiceEndfrom the UI thread while a worker thread was still blocked inWSALookupServiceNext. Classic race.
Fix it
1. Confirm it's a thread race, not a network fault
Open your app's logs and grep for the error, then correlate timestamps with any "cancel", "stop", "timeout", or "abort" events. If every 0x00002777 lines up with a cancel action, you've got your answer. You can also capture a trace with Wireshark or Microsoft Network Monitor — if you see WSAECANCELLED but no DNS/NBNS/mDNS traffic dying on the wire, it's local, not the network.
2. Stop calling WSALookupServiceEnd from the wrong thread
The handle belongs to the thread that opened it. Don't end it from a UI callback or timer while the worker is still inside WSALookupServiceNext. Instead, set an atomic "cancel requested" flag and let the worker exit its own loop:
// worker thread
while (!g_cancelRequested) {
WSAQUERYSET qs = {0};
qs.dwSize = sizeof(qs);
int rc = WSALookupServiceNext(hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR,
&dwBufLen, &qs);
if (rc == SOCKET_ERROR) {
int err = WSAGetLastError();
if (err == WSA_E_NO_MORE) break; // done, clean exit
if (err == WSAECANCELLED) break; // cancelled from elsewhere, bail
// anything else: log and bail
break;
}
ProcessResult(&qs);
}
WSALookupServiceEnd(hLookup); // only the owning thread closes it
3. Treat WSAECANCELLED as a normal exit, not an error
Half the bugs I see are just this. Apps log WSAECANCELLED as a fatal condition, pop an error dialog, and sometimes retry the scan in a loop — which makes the race worse. WSAECANCELLED means "the operation was deliberately stopped", same category as WSA_E_NO_MORE. Handle it as a clean shutdown and move on.
4. If you're using getaddrinfo, stop mixing it with WSALookupServiceEnd
getaddrinfo wraps the WSA lookup internally. You cannot call WSALookupServiceEnd against it — there's no handle to hand it. If you're seeing 0x00002777 from a getaddrinfo call, the actual caller is something else, likely a Bluetooth or SSDP enumerator in the same process. Check your DLLs.
5. Driver-level case: third-party Bluetooth/Wi-Fi stacks
Some OEM Bluetooth suites (Broadcom, older Intel BT drivers) have service-discovery helpers that cancel lookups aggressively when a device drops off. If you can't change the calling code because it's in a vendor DLL, check for updates. Broadcom's 12.x BT stack had a known cancellation-on-disconnect bug that produced exactly this error against Windows 10 1809+.
Alternatives if that doesn't clear it
- Serialise lookups per handle. If two threads share a lookup handle, don't. One handle, one thread. Wrap it in a critical section or a mutex if you must share state.
- Reset Winsock. Rare, but if the stack is genuinely wedged from a botched cancel, a reboot or
netsh winsock resetclears it. Reboot after. - Set LUP_FLUSHCACHE off. Some providers behave badly with cached lookups being cancelled. Try the lookup without cache flags and see if the error goes away — this tells you it's the provider, not your code.
- Rebuild against a newer SDK. Windows 10 20H1+ shipped fixes to the WSA lookup path for cancellation timing. Old pre-2019 SDKs have known races here.
- Kill the retry loop. If your app retries the lookup on WSAECANCELLED, stop. Retries generate more cancellations and can look like a DoS to the local provider.
Prevention
Treat every WSA lookup handle as thread-owned. Open it in one thread, use it in that thread, close it in that thread. Cancellation is a signal, not a handle-destroy. If you need to abort a scan, set a flag and let the worker notice — don't reach in and call WSALookupServiceEnd from outside. That single rule kills 90% of WSAECANCELLED reports, and saves you from the other 10% turning into a support ticket about "flaky Bluetooth" that was never flaky at all.