microsoft/semantic-kernel · error · KernelException

The derived DeactivateAsync method failed to complete for st

Error message

The derived DeactivateAsync method failed to complete for step {this.Name}.

What it means

Thrown by LocalProxy.DeinitializeStepAsync when the reflection-invoked DeactivateAsync method on the proxy step's inner type returns null instead of a ValueTask. The code uses methodInfo.Invoke to call the derived DeactivateAsync(KernelProcessStepExternalContext) and expects a non-null ValueTask result. A null return means the method's return type is not ValueTask or the invocation produced no result.

Source

Thrown at dotnet/src/Experimental/Process.LocalRuntime/LocalProxy.cs:101

    /// <summary>
    /// Deinitialization of the Proxy Step, calling <see cref="KernelProxyStep.DeactivateAsync(KernelProcessStepExternalContext)"/>
    /// </summary>
    /// <returns></returns>
    public override async Task DeinitializeStepAsync()
    {
        MethodInfo? derivedMethod = this._stepInfo.InnerStepType.GetMethod(
            nameof(KernelProxyStep.DeactivateAsync),
            BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
            binder: null,
            types: [typeof(KernelProcessStepExternalContext)],
            modifiers: null);

        if (derivedMethod != null && this._stepInstance != null)
        {
            var context = new KernelProcessStepExternalContext(this.ExternalMessageChannel);
            ValueTask deactivateTask =
                (ValueTask?)derivedMethod.Invoke(this._stepInstance, [context]) ??
                throw new KernelException($"The derived DeactivateAsync method failed to complete for step {this.Name}.").Log(this._logger);

            await deactivateTask.ConfigureAwait(false);
        }
    }
}

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Ensure the custom KernelProxyStep subclass's DeactivateAsync method returns ValueTask and accepts a KernelProcessStepExternalContext parameter.
  2. Match the exact method signature: 'protected internal virtual ValueTask DeactivateAsync(KernelProcessStepExternalContext context)'.
  3. If no custom deactivation logic is needed, remove the override so the base implementation is used.

Example fix

// before - returns Task instead of ValueTask
public override Task DeactivateAsync(KernelProcessStepExternalContext context) { ... }
// after - returns ValueTask
public override ValueTask DeactivateAsync(KernelProcessStepExternalContext context) { ... }
Defensive patterns

Strategy: type-guard

Type guard

// Verify the step type's DeactivateAsync returns ValueTask before registering
var method = typeof(TProxyStep).GetMethod(nameof(KernelProxyStep.DeactivateAsync),
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
    binder: null, types: [typeof(KernelProcessStepExternalContext)], modifiers: null);
if (method?.ReturnType != typeof(ValueTask))
{
    throw new InvalidOperationException($"{typeof(TProxyStep).Name}.DeactivateAsync must return ValueTask.");
}

Prevention

When it happens

Trigger: The inner step type (KernelProcessProxyStep subclass) declares a DeactivateAsync method matching the expected signature but its return type is not ValueTask (e.g., returns Task or void), causing the cast to ValueTask? to yield null.

Common situations: Implementing a custom KernelProxyStep with a DeactivateAsync override that returns Task instead of ValueTask; signature mismatch (wrong parameter types); framework version changes to the expected return type.

Related errors


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