microsoft/semantic-kernel · error · KernelException

Agent Failure - Run terminated

Error message

Agent Failure - Run terminated: {run.Status} [{run.Id}]: {run.LastError?.Message ?? "Unknown"}

What it means

In the non-streaming InvokeAsync loop, after each poll the run's status is checked; if it is terminal but not Completed (e.g., Failed, Cancelled, Expired, Incomplete), a KernelException is thrown carrying the run id, status, and last error message.

Solutions

  1. Inspect run.LastError.Message in the exception to find the root cause.
  2. Make invoked function tools robust (catch internal errors, return a string result) so they don't fail the run.
  3. Retry with exponential backoff for transient failures (rate limits, 5xx).
  4. Increase run/timeout settings or reduce tool output size if the run expired or hit length limits.

Example fix

// before
await foreach (var r in agent.InvokeAsync(thread)) { /* no error handling */ }
// after
try {
    await foreach (var r in agent.InvokeAsync(thread)) { /* ... */ }
}
catch (KernelException ex) when (ex.Message.Contains("Run terminated")) {
    logger.LogError(ex, "Assistant run failed; retrying");
    await Task.Delay(backoff);
}
Defensive patterns

Strategy: retry

Try / catch

int attempts = 0;
retry:
try { await foreach (var r in agent.InvokeAsync(thread)) { /* ... */ } }
catch (KernelException ex) when (ex.Message.Contains("Run terminated") && attempts++ < 3) {
    await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempts)));
    goto retry;
}

Prevention

When it happens

Trigger: An OpenAI assistant run ends in a non-success terminal state: a function tool threw, the run exceeded max iterations/tokens, it hit a content-policy block, rate limit, or expired before completing.

Common situations: A kernel function tool raises an exception the assistant surface treats as a run failure; long-running tools causing expiration; quota/rate-limit responses; malformed tool outputs.

Related errors


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

Appendix: source

Thrown at dotnet/src/Agents/OpenAI/Internal/AssistantThreadActions.cs:164

        FunctionCallsProcessor functionProcessor = new(logger);
        // This matches current behavior.  Will be configurable upon integrating with `FunctionChoice` (#6795/#5200)
        FunctionChoiceBehaviorOptions functionOptions = new() { AllowConcurrentInvocation = true, AllowParallelCalls = true };

        // Evaluate status and process steps and messages, as encountered.
        HashSet<string> processedStepIds = [];
        Dictionary<string, FunctionResultContent> functionSteps = [];
        do
        {
            // Check for cancellation
            cancellationToken.ThrowIfCancellationRequested();

            // Poll run and steps until actionable
            await PollRunStatusAsync().ConfigureAwait(false);

            // Is in terminal state?
            if (run.Status.IsTerminal && run.Status != RunStatus.Completed)
            {
                throw new KernelException($"Agent Failure - Run terminated: {run.Status} [{run.Id}]: {run.LastError?.Message ?? "Unknown"}");
            }

            List<RunStep> steps = [];
            await foreach (var step in client.GetRunStepsAsync(run.ThreadId, run.Id, cancellationToken: cancellationToken).ConfigureAwait(false))
            {
                steps.Add(step);
            }

            // Is tool action required?
            if (run.Status == RunStatus.RequiresAction)
            {
                logger.LogOpenAIAssistantProcessingRunSteps(nameof(InvokeAsync), run.Id, threadId);

                // Execute functions in parallel and post results at once.
                FunctionCallContent[] functionCalls = steps.SelectMany(step => ParseFunctionStep(agent, step)).ToArray();
                if (functionCalls.Length > 0)
                {
                    // Emit function-call content

View on GitHub (pinned to c028a0c7dc)