microsoft/semantic-kernel · error · KernelException

Agent Failure - Run terminated: {run.Status} [{run.Id}]: {ru

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.

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)

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.