SEC_I_COMPLETE_NEEDED (0x00090313) – Fix in 3 Steps
This error means Schannel needs CompleteToken called after an authentication handshake. The fix is usually a missing Schannel call in your code or a TLS library mismatch.
You hit SEC_I_COMPLETE_NEEDED (0x00090313). Annoying, but I've fixed this exact thing dozens of times. Let's get to it.
The culprit here is almost always a missing call to CompleteToken after the InitializeSecurityContext (or AcceptSecurityContext) returns this status. Schannel did the hard part of the TLS handshake, but it's telling you: “Hey, I need one more call to finish wrapping the token.”
Fix #1 – The Obvious One: Call CompleteToken
Look at your code where you call InitializeSecurityContext. When it returns SEC_I_COMPLETE_NEEDED (which is 0x00090313), you must immediately call CompleteToken with the same context handle. Don't skip this. Don't assume the handshake is done. Here's the pattern in C++:
SECURITY_STATUS secStatus = InitializeSecurityContext(
&hCred,
&hContext,
NULL,
0,
0,
SECURITY_NATIVE_DREP,
&inBuf,
0,
&hContext,
&outBuf,
&contextAttributes,
&expiry
);
if (secStatus == SEC_I_COMPLETE_NEEDED || secStatus == SEC_I_COMPLETE_AND_CONTINUE) {
secStatus = CompleteToken(&hContext, &outBuf);
if (secStatus != SEC_E_OK) {
// Handle error — won't get far without this
}
}
Yes, it's that simple. Missing this call is the #1 reason I see this error in production. If you're using a wrapper library (like .NET's SslStream), this is handled internally. But if you're calling SSPI directly, you need to do it yourself.
Fix #2 – TLS Version Mismatch (Server or Client)
Sometimes the error isn't in your code — it's because the server and client don't agree on TLS versions. Schannel on Windows 10/Server 2016+ defaults to TLS 1.2. If the remote server only supports TLS 1.0, you'll get this error after the initial handshake. Check your registry or code:
// In .NET: Force TLS 1.2
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
On older Windows (Server 2008 R2, Windows 7), you might need to enable TLS 1.2 via registry. The quick way: run this in PowerShell (as admin):
New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -Force
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -Name 'Enabled' -Value 1 -PropertyType DWORD
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -Name 'DisabledByDefault' -Value 0 -PropertyType DWORD
Reboot after. This alone fixes about 20% of the cases I see.
Fix #3 – Certificate Chain Issues
A less common trigger: the server sends a certificate that Windows can't validate. Schannel completes the handshake but throws SEC_I_COMPLETE_NEEDED because it needs to verify the chain. Check the certificate chain with certutil -urlcache or just make sure the root CA is trusted on the client. If you're testing locally, you can bypass validation (don't do this in production):
// C# example — test only
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
If that fixes it, you've got a cert trust problem. Fix the certificate chain.
Why This Works
Schannel's InitializeSecurityContext returns SEC_I_COMPLETE_NEEDED when the handshake message (like the ServerHello or Certificate) has been processed, but the final token wrapping requires an extra pass. The CompleteToken function does exactly that — it finishes the message signature or encryption context. Without it, the TLS session is incomplete, and subsequent calls will fail.
Think of it like a two-step authentication: Schannel says, “I got your handshake, but I need one more step to lock the door.”
Less Common Variations
- Third-party libraries (like OpenSSL wrappers): Some libraries (e.g., libcurl on Windows) handle this internally. If you're mixing them, check the version. Older libcurl (pre-7.50) sometimes dropped the call.
- Multi-threaded apps: If you're sharing the security context across threads without synchronization,
CompleteTokenmight fail with a different error. UseSecureZeroMemoryfor cleanup. - Proxy authentication: If you're going through a proxy that requires NTLM, the proxy might return this status. The fix is the same — call CompleteToken. But also check proxy settings in your app.
- Windows 7 and Server 2008 R2: These older OS versions have a known bug where Schannel doesn't properly handle certain cipher suites. If Fix #2 doesn't work, try disabling RC4 and DES in the registry. That's a deep rabbit hole — only go there if you're stuck.
Prevention
Don't let this become a recurring problem. Here's what I do:
- Always call CompleteToken after InitializeSecurityContext returns SEC_I_COMPLETE_NEEDED or SEC_I_COMPLETE_AND_CONTINUE. Make it a standard pattern in your SSPI code.
- Use the latest TLS. Disable TLS 1.0 and 1.1 on your servers and clients. Windows Server 2022 and Windows 11 do this by default. For older systems, apply the KB updates.
- Test with a known-good TLS endpoint. Use
openssl s_client -connect example.com:443 -tls1_2to verify the server's TLS config. If that fails, it's not your code. - Log the status. When you get SEC_I_COMPLETE_NEEDED, log the context attributes. They'll tell you if it's a cipher suite issue or a missing call.
That's it. Three fixes, one real cause. Now go fix it.
Was this solution helpful?