jstedfast/MailKit · error · ObjectDisposedException

MD4

Error message

MD4

What it means

ComputeHash(byte[]) throws ObjectDisposedException named "MD4" when the hasher has already been disposed. MD4 implements IDisposable like other HashAlgorithms; after Dispose(), its internal state is cleared and further hashing is invalid. This guards against silently producing wrong hashes from a reset/cleared instance.

Solutions

  1. Create a new MD4 instance for each hash operation instead of reusing a disposed one.
  2. Remove the Dispose call (or the using block) on the path that still needs the hasher, and manage lifetime in one owner.
  3. If reuse is needed, wrap access so Dispose happens only after all hashing completes (e.g. lazy dispose at service shutdown).
  4. Optionally catch ObjectDisposedException at the boundary to detect lifetime bugs, but prefer fixing ownership.

Example fix

// before
using (var md4 = new MD4()) { } 
md4.ComputeHash(data); // disposed
// after
using (var md4 = new MD4()) {
    var hash = md4.ComputeHash(data);
}
Defensive patterns

Strategy: validation

Validate before calling

if (md4 == null) throw new InvalidOperationException("MD4 not created");
// avoid the situation: do not keep using an instance after 'using' or Dispose(); create a fresh one per operation.

Type guard

static bool IsUsable(MD4 md4) => md4 != null; // MD4 does not expose a disposed flag; enforce single-owner lifetime instead of probing

Try / catch

try { hash = md4.ComputeHash(data); }
catch (ObjectDisposedException) { md4 = new MD4(); hash = md4.ComputeHash(data); } // recovery: recreate; prefer fixing lifetime ownership

Prevention

When it happens

Trigger: Calling ComputeHash(buffer) (or the offset/count overload, which routes here after its own checks) on an MD4 instance after a using block ended or Dispose() was called explicitly; caching an MD4 instance in a static field while also using it in a using statement.

Common situations: Long-lived NTLM helper class that holds an MD4 across requests while some code path disposes it; reusing a hasher created inside a using scope; double-dispose patterns where a wrapper disposes the hasher the caller still uses.

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/f39141c68c810b19. Report an issue: GitHub.

Appendix: source

Thrown at MailKit/Security/Ntlm/MD4.cs:299

			state [0] += a;
			state [1] += b;
			state [2] += c;
			state [3] += d;
		}

		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 (nameof (MD4));

			HashCore (buffer, offset, count);
			hashValue = HashFinal ();
			Initialize ();

			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)
		{

View on GitHub (pinned to 9d3859a785)