jstedfast/MailKit · error · ObjectDisposedException
ObjectDisposedException
Error message
ObjectDisposedException
What it means
CheckDisposed throws ObjectDisposedException(nameof(DuplexStream)) when Read, ReadAsync, Write, WriteAsync, Flush or FlushAsync is called after Dispose has run. It protects against using an underlying duplex pipe whose resources were already released.
Solutions
- Ensure all I/O on the stream completes (await tasks) before Dispose; check for unawaited async operations.
- Keep the stream alive for the duration of its use; widen the using scope or switch to explicit Dispose at the correct point.
- If lifecycle is racy, check for disposal via your own flag or catch ObjectDisposedException around late operations.
Example fix
// before
using (var duplex = new DuplexStream (a, b)) {
var task = duplex.ReadAsync (buffer, 0, buffer.Length);
}
var n = await task; // throws
// after
using (var duplex = new DuplexStream (a, b)) {
var n = await duplex.ReadAsync (buffer, 0, buffer.Length);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!ownStream) { /* track lifetime with your own disposed flag before use */ }
private bool disposed;
if (disposed) return; // caller-side guard Try / catch
try { await stream.ReadAsync (buffer, 0, buffer.Length); }
catch (ObjectDisposedException) { /* stream already disposed: re-acquire or abort */ } Prevention
- Await all async I/O before leaving the using scope that owns the stream.
- Do not store streams in long-lived fields if they are created in short-lived using blocks.
- Serialize access and disposal: one owner decides when the stream dies.
When it happens
Trigger: Calling any read/write/flush method on a DuplexStream after Dispose() (or a 'using' block scope that has exited), including async continuations that run after the using block ends.
Common situations: Firing async read/write tasks and disposing the stream before they complete (e.g. cancellation without awaiting); storing the stream in a field and reusing it after a using scope; double-dispose followed by retry logic.
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/9809cf8783fcc68e.
Report an issue: GitHub.
Appendix: source
Thrown at MailKit/DuplexStream.cs:170
get { throw new NotSupportedException (); }
}
static void ValidateArguments (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 || count > (buffer.Length - offset))
throw new ArgumentOutOfRangeException (nameof (count));
}
void CheckDisposed ()
{
if (disposed)
throw new ObjectDisposedException (nameof (DuplexStream));
}
/// <summary>
/// Reads a sequence of bytes from the stream and advances the position
/// within the stream by the number of bytes read.
/// </summary>
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many
/// bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The buffer offset.</param>
/// <param name="count">The number of bytes to read.</param>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="buffer"/> is <see langword="null" />.
/// </exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// <para><paramref name="offset"/> is less than zero or greater than the length of <paramref name="buffer"/>.</para>
/// <para>-or-</para>
/// <para>The <paramref name="buffer"/> is not large enough to contain <paramref name="count"/> bytes startingView on GitHub (pinned to 9d3859a785)