microsoft/semantic-kernel · error · ObjectDisposedException

Cannot access a disposed object.

Error message

Cannot access a disposed object.

What it means

Thrown by GetValueAsync (and Cancel) on OrchestrationResult<TValue> after the instance has been disposed. OrchestrationResult is IDisposable (it owns a CancellationTokenSource and a TaskCompletionSource); once Dispose runs, _isDisposed is true and any further access throws ObjectDisposedException.

Source

Thrown at dotnet/src/Agents/Orchestration/OrchestrationResult.cs:66

    /// </summary>
    public TopicId Topic => this._context.Topic;

    /// <summary>
    /// Asynchronously retrieves the orchestration result value.
    /// If a timeout is specified, the method will throw a <see cref="TimeoutException"/>
    /// if the orchestration does not complete within the allotted time.
    /// </summary>
    /// <param name="timeout">An optional <see cref="TimeSpan"/> representing the maximum wait duration.</param>
    /// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
    /// <returns>A <see cref="ValueTask{TValue}"/> representing the result of the orchestration.</returns>
    /// <exception cref="ObjectDisposedException">Thrown if this instance has been disposed.</exception>
    /// <exception cref="TimeoutException">Thrown if the orchestration does not complete within the specified timeout period.</exception>
    public async ValueTask<TValue> GetValueAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
    {
#if !NETCOREAPP
        if (this._isDisposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
#else
        ObjectDisposedException.ThrowIf(this._isDisposed, this);
#endif

        this._logger.LogOrchestrationResultAwait(this.Orchestration, this.Topic);

        if (timeout.HasValue)
        {
#if NET
            try
            {
                await this._completion.Task.WaitAsync(timeout.Value, cancellationToken).ConfigureAwait(false);
            }
            catch (TimeoutException)
            {
                this._logger.LogOrchestrationResultTimeout(this.Orchestration, this.Topic);
                throw;

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Call GetValueAsync before the result is disposed — do not wrap it in 'using' if you await afterwards.
  2. Avoid calling Dispose until you have retrieved the value.
  3. Track disposal state so a disposed result is not accessed again.

Example fix

// before
using var result = await orchestration.InvokeAsync(input);
var value = await result.GetValueAsync(); // 'using' may dispose before use on some flows

// after — dispose only after retrieving the value
var result = await orchestration.InvokeAsync(input);
try { var value = await result.GetValueAsync(); }
finally { result.Dispose(); }
Defensive patterns

Strategy: validation

Validate before calling

// Retrieve the value before disposing; dispose only afterwards
var result = await orchestration.InvokeAsync(input);
try { var value = await result.GetValueAsync(); }
finally { result.Dispose(); }

Try / catch

try { return await result.GetValueAsync(); }
catch (ObjectDisposedException) { /* result already disposed; recreate orchestration */ throw; }

Prevention

When it happens

Trigger: using var result = orchestration.InvokeAsync(...); await result.GetValueAsync(); where Dispose fires before GetValueAsync completes (e.g. via 'using' scoping, or explicit Dispose before await), or reusing a disposed result.

Common situations: Wrapping the result in a 'using' block and awaiting outside that block; calling GetValueAsync twice across a Dispose; DI scopes disposing the result early.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/d7a2c2be8be0e292. Report an issue: GitHub.