jstedfast/MailKit · error · ObjectDisposedException
HashAlgorithm
Error message
HashAlgorithm
What it means
HMACMD5.ComputeHash(buffer, offset, count) throws an ArgumentOutOfRange with the name 'HashAlgorithm' when the instance has already been disposed. MailKit's NTLM HMACMD5 mimics System.Security.Cryptography.HashAlgorithm, which signals a used-up object via ObjectDisposedException('HashAlgorithm'). Once Dispose() runs, the internal MD5 state is gone and hashing can no longer proceed.
Solutions
- Create a new HMACMD5 instance for each ComputeHash call instead of reusing a disposed one
- Move ComputeHash calls inside the using/undisposed scope
- Verify no double-dispose via IDisposable cascades (e.g., SaslMechanism disposing the HMAC early)
Example fix
// before
using (var hmac = new HMACMD5 (key)) { }
byte[] hash = hmac.ComputeHash (data); // ObjectDisposedException
// after
using (var hmac = new HMACMD5 (key)) {
byte[] hash = hmac.ComputeHash (data);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (hmac == null) throw new InvalidOperationException ("HMACMD5 not created");
// MailKit's HMACMD5 exposes no public IsDisposed; track it yourself:
bool disposed = false;
if (disposed) throw new InvalidOperationException ("HMAC instance already disposed"); Type guard
static bool IsUsable (HMACMD5? hmac) => hmac != null && !ReferenceEquals (hmac, null);
Try / catch
try {
byte[] hash = hmac.ComputeHash (data);
} catch (ObjectDisposedException) {
hmac = new HMACMD5 (key);
byte[] hash = hmac.ComputeHash (data);
} Prevention
- Create a new HMACMD5 per hashing operation; instances are cheap
- Scope instance and usage inside the same using block
- Never cache hash instances across authentication attempts
When it happens
Trigger: Calling ComputeHash after HMACMD5.Dispose() was invoked (explicitly or via using block); reusing a single instance across two authentication attempts where the first attempt disposed it.
Common situations: Reusing one HMACMD5 instance for multiple NTLM auth rounds in a connection pool; wrapping the hash in 'using' then calling ComputeHash afterwards; disposal triggered by an outer authenticate() finally block.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of jstedfast/MailKit@9d3859a785 (2026-09-15).
Data as JSON: /api/errors/9bcc391c94e7ba0e.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/Security/Ntlm/HMACMD5.cs:112
public void Clear ()
{
Dispose (false);
}
public byte[] ComputeHash (byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException (nameof (buffer));
if (offset < 0 || offset > buffer.Length)
throw new ArgumentOutOfRangeException (nameof (offset));
if (count < 0 || offset > buffer.Length - count)
throw new ArgumentOutOfRangeException (nameof (count));
if (disposed)
throw new ObjectDisposedException ("HashAlgorithm");
HashCore (buffer, offset, count);
hashValue = HashFinal ();
return hashValue;
}
public byte[] ComputeHash (byte[] buffer)
{
if (buffer == null)
throw new ArgumentNullException (nameof (buffer));
return ComputeHash (buffer, 0, buffer.Length);
}
public byte[] ComputeHash (Stream inputStream)
{
// don't read stream unless object is ready to useView on GitHub (pinned to 9d3859a785)