jstedfast/MailKit · error · ArgumentNullException
negotiate
Error message
negotiate
What it means
The NtlmAuthenticateMessage constructor throws ArgumentNullException when the negotiate parameter is null. The NTLM AUTHENTICATE message requires the preceding NEGOTIATE message to build the Type3 payload, so it is a mandatory argument.
Solutions
- Create the NtlmNegotiateMessage before constructing the authenticate message.
- Check the negotiate value for null before calling the constructor.
- If negotiate comes from a parse/decode call, handle its failure path instead of passing null through.
Example fix
// before var auth = new NtlmAuthenticateMessage(negotiate, challenge, user, pass, domain, ws); // negotiate is null // after if (negotiate == null) negotiate = new NtlmNegotiateMessage(); var auth = new NtlmAuthenticateMessage(negotiate, challenge, user, pass, domain, ws);
Defensive patterns
Strategy: validation
Validate before calling
if (negotiate == null) throw new InvalidOperationException("NTLM negotiate message must be created before the authenticate message.");
var auth = new NtlmAuthenticateMessage(negotiate, challenge, userName, password, domain, workstation); Type guard
static bool CanBuildAuthMessage(NtlmNegotiateMessage n, NtlmChallengeMessage c) => n != null && c != null;
Try / catch
try {
auth = new NtlmAuthenticateMessage(negotiate, challenge, userName, password, domain, workstation);
} catch (ArgumentNullException ex) when (ex.ParamName == "negotiate") {
// recreate the Type1 message and retry the handshake
negotiate = new NtlmNegotiateMessage();
auth = new NtlmAuthenticateMessage(negotiate, challenge, userName, password, domain, workstation);
} Prevention
- Keep the handshake order explicit: create NEGOTIATE, receive CHALLENGE, then build AUTHENTICATE.
- Check nullable results from any parse/decode call before forwarding them.
- Enable nullable reference types so null flows are flagged at compile time.
When it happens
Trigger: Calling new NtlmAuthenticateMessage(null, challenge, ...) — e.g. the negotiate message was never created or a method returned null on a parse failure.
Common situations: Siloed NTLM handshake code where the Type1 message creation is skipped or its parse failed silently; refactors that reorder the handshake steps.
Related errors
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/393437f0a65818ec.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs:45
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b38c36ed-2804-4868-a9ff-8dd3182128e4
using System;
using System.Text;
using System.Diagnostics.CodeAnalysis;
namespace MailKit.Security.Ntlm {
class NtlmAuthenticateMessage : NtlmMessageBase
{
static readonly byte[] Z16 = new byte[16];
readonly NtlmNegotiateMessage? negotiate;
readonly NtlmChallengeMessage? challenge;
byte[] clientChallenge;
public NtlmAuthenticateMessage (NtlmNegotiateMessage negotiate, NtlmChallengeMessage challenge, string userName, string password, string domain, string workstation) : base (3)
{
if (negotiate == null)
throw new ArgumentNullException (nameof (negotiate));
if (challenge == null)
throw new ArgumentNullException (nameof (challenge));
if (userName == null)
throw new ArgumentNullException (nameof (userName));
if (password == null)
throw new ArgumentNullException (nameof (password));
clientChallenge = NtlmUtils.NONCE (8);
this.negotiate = negotiate;
this.challenge = challenge;
if (!string.IsNullOrEmpty (domain)) {
Domain = domain;
} else if ((challenge.Flags & NtlmFlags.TargetTypeDomain) != 0) {
// The server is domain-joined, so the TargetName will be the domain.View on GitHub (pinned to 9d3859a785)