microsoft/semantic-kernel · warning · RuntimeError
The invocation has already been completed.
Error message
The invocation has already been completed.
What it means
OrchestrationResult.cancel() refuses to cancel after the result event is already set, i.e. the invocation has already completed (success, failure, or prior cancel). Cancel-after-complete is a no-op at best and a logic error, so it raises.
Source
Thrown at python/semantic_kernel/agents/orchestration/orchestration_base.py:78
if self.value is None:
if self.cancellation_token.is_cancelled():
raise RuntimeError("The invocation was canceled before it could complete.")
if self.exception is not None:
raise self.exception
raise RuntimeError("The invocation did not produce a result.")
return self.value
def cancel(self) -> None:
"""Cancel the invocation.
This method will cancel the invocation.
Actors that have received messages will continue to process them, but no new messages will be processed.
"""
if self.cancellation_token.is_cancelled():
raise RuntimeError("The invocation has already been canceled.")
if self.event.is_set():
raise RuntimeError("The invocation has already been completed.")
self.cancellation_token.cancel()
self.event.set()
@experimental
class OrchestrationBase(ABC, Generic[TIn, TOut]):
"""Base class for multi-agent orchestration."""
t_in: type[TIn] | None = None
t_out: type[TOut] | None = None
def __init__(
self,
members: list[Agent],
name: str | None = None,
description: str | None = None,
input_transform: Callable[[TIn], Awaitable[DefaultTypeAlias] | DefaultTypeAlias] | None = None,View on GitHub (pinned to c028a0c7dc)
Solutions
- Only cancel when the run is still in flight: check `if not result.event.is_set(): result.cancel()`.
- Move cancellation out of the success/completion path; only cancel on actual abort/timeout.
- Await get() with a timeout and cancel only if get() raised TimeoutError.
Example fix
// before
value = await result.get()
result.cancel() # raises 'already been completed'
// after
try:
value = await result.get(timeout=30)
except asyncio.TimeoutError:
if not result.event.is_set():
result.cancel() Defensive patterns
Strategy: validation
Validate before calling
# Only cancel while the run is still in flight
if not result.event.is_set():
result.cancel() Type guard
def result_still_running(result) -> bool:
return not result.event.is_set() Prevention
- Don't cancel on the success path; only on actual abort/timeout.
- Use get(timeout=...) and cancel only if it times out.
- Guard cancel() with `if not result.event.is_set()`.
When it happens
Trigger: Calling `result.cancel()` after the orchestration already finished and set the event — e.g. after `await result.get()` returned, or after a prior cancel()/completion.
Common situations: A finally block that cancels unconditionally even on the success path. A timeout handler firing after the run already completed. Cancel wired into both success and error cleanup.
Related errors
- The invocation was canceled before it could complete.
- The invocation has already been canceled.
- The invocation did not produce a result.
- Cannot access a disposed object.
- A complete listen_for condition is required for orchestratio
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/cd8687c4709cccc9.
Report an issue: GitHub.