microsoft/semantic-kernel · warning · RuntimeError

The invocation has already been canceled.

Error message

The invocation has already been canceled.

What it means

OrchestrationResult.cancel() first checks whether the cancellation token is already canceled and refuses a double-cancel. Cancellation is idempotent by design; calling cancel() twice is a usage error.

Source

Thrown at python/semantic_kernel/agents/orchestration/orchestration_base.py:76

        else:
            await self.event.wait()

        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,

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Guard cancel() with `if not result.cancellation_token.is_cancelled(): result.cancel()`.
  2. Centralize cancellation in one place (e.g. a single finally block) rather than canceling from multiple handlers.
  3. Track cancellation state yourself and skip redundant calls.

Example fix

// before
result.cancel()
result.cancel()  # raises 'already been canceled'

// after
result.cancel()
if not result.cancellation_token.is_cancelled():
    result.cancel()
Defensive patterns

Strategy: validation

Validate before calling

# Idempotent cancel
if not result.cancellation_token.is_cancelled():
    result.cancel()

Type guard

def result_already_canceled(result) -> bool:
    return result.cancellation_token.is_cancelled()

Prevention

When it happens

Trigger: Calling `result.cancel()` more than once on the same OrchestrationResult instance.

Common situations: A cleanup/finally block that cancels, combined with another cancel on timeout or error. Multiple components (e.g. a timeout handler and a shutdown handler) both canceling the same result.

Related errors


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