microsoft/semantic-kernel · warning · RuntimeError

The invocation was canceled before it could complete.

Error message

The invocation was canceled before it could complete.

What it means

OrchestrationResult.get() waits on an asyncio.Event; when the event is set with value still None, it checks the cancellation token. If the invocation was canceled (cancel() set the token), get() raises this RuntimeError instead of returning a partial/empty result. It signals the run was aborted before producing output.

Source

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

        If a timeout is specified, the method will wait for the result for the specified time.
        If the result is not available within the timeout, a TimeoutError will be raised but the
        invocation will not be aborted.

        Args:
            timeout (int | None): The timeout (seconds) for getting the result. If None, wait indefinitely.

        Returns:
            TOut: The result of the invocation.
        """
        if timeout is not None:
            await asyncio.wait_for(self.event.wait(), timeout=timeout)
        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()

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Treat cancellation as expected: catch RuntimeError (or asyncio.CancelledError) around get() when you may cancel.
  2. Avoid calling get() after cancel(); if you cancel, don't expect a value.
  3. Use get(timeout=...) so asyncio.wait_for raises TimeoutError instead, leaving the run running.

Example fix

// before
result = await orch.invoke(task, runtime)
result.cancel()
value = await result.get()  # raises 'canceled before it could complete'

// after
result = await orch.invoke(task, runtime)
result.cancel()
try:
    value = await result.get()
except RuntimeError:
    value = None  # expected after cancel
Defensive patterns

Strategy: try-catch

Validate before calling

# Only read the result if not canceled
if not result.cancellation_token.is_cancelled():
    value = await result.get()

Type guard

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

Try / catch

try:
    value = await result.get()
except RuntimeError as e:
    if "canceled" in str(e):
        value = None  # expected after cancel
    else:
        raise

Prevention

When it happens

Trigger: Calling `orchestration_result.cancel()` (or the runtime cancelling) and then awaiting `await result.get()`. Also when get() is awaited after the event was set purely by cancellation.

Common situations: A user-initiated cancellation (timeout, Ctrl-C handling) followed by reading the result. A parent task canceling the orchestration's task. Calling cancel() then get() to inspect state.

Related errors


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