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
- Ensure the custom KernelProxyStep subclass's DeactivateAsync method returns ValueTask and accepts a KernelProcessStepExternalContext parameter.
- Match the exact method signature: 'protected internal virtual ValueTask DeactivateAsync(KernelProcessStepExternalContext context)'.
- 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
- Always match the KernelProxyStep.DeactivateAsync signature: ValueTask return type with KernelProcessStepExternalContext parameter.
- Add a unit test that reflects on your proxy step types to verify method signatures.
- Avoid changing the return type when overriding virtual methods.
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
- The ActivateAsync method for the KernelProcessStep could not
- The ActivateAsync method failed to complete.
- Attempt to build a workflow node from step with no Id
- This method is not implemented in this test fixture.
- Function Invoke not found in plugin {this.Name}
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/384fd33f6a027adb.
Report an issue: GitHub.