github/copilot-sdk · error · CanvasError

canvas_action_no_handler

canvas_action_no_handler

Error message

No handler implemented for this canvas action

What it means

CanvasProvider.on_action is the hook for handling non-lifecycle canvas actions declared by a canvas. The base-class default implementation raises CanvasError.no_handler() (code 'canvas_action_no_handler'), signalling that the provider subclass did not override on_action for an action it declared. If your canvas declares actions, you must implement this method.

Solutions

  1. Override on_action in your CanvasProvider subclass and implement the declared actions
  2. Route on ctx.request/ctx.action to the correct handler and return a JSON-serializable result
  3. Remove any declared actions your provider does not implement
  4. Add a guard/deployment check that every declared canvas action has a matching on_action branch

Example fix

// before
class MyProvider(CanvasProvider):
    async def on_open(self, ctx): ...
    # on_action not overridden

// after
class MyProvider(CanvasProvider):
    async def on_action(self, ctx):
        if ctx.action == "submit":
            return {"ok": True}
        raise CanvasError.no_handler()
Defensive patterns

Strategy: try-catch

Validate before calling

declared = {a["name"] for a in canvas_manifest.get("actions", [])}
if declared and not isinstance(provider.on_action, type(CanvasProvider.on_action)):
    raise RuntimeError("canvas declares actions but provider does not override on_action")

Type guard

def implements_on_action(provider) -> bool:
    return type(provider).on_action is not CanvasProvider.on_action

Try / catch

try:
    result = await provider.on_action(ctx)
except CanvasError as e:
    if e.code == "canvas_action_no_handler":
        result = {"error": f"action {ctx.action} not supported"}

Prevention

When it happens

Trigger: A user or agent invokes an action on a canvas whose provider subclass does not override on_action — the declared action reaches the base-class stub, which always raises.

Common situations: Developers define canvas actions in their canvas manifest but only override lifecycle hooks (on_open/on_close); a new action is added to the canvas while the provider code is not deployed/updated; the provider is wired to a canvas whose action set changed.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/c7e986177d9a7844. Report an issue: GitHub.

Appendix: source

Thrown at python/copilot/canvas.py:192

        **Experimental.** This type is part of an experimental wire-protocol
        surface and may change or be removed in future SDK or CLI releases.
    """

    @abstractmethod
    async def on_open(self, ctx: CanvasProviderOpenRequest) -> CanvasProviderOpenResult:
        """Open a new canvas instance.

        May raise :class:`CanvasError` to surface a structured failure to
        the host.
        """

    async def on_close(self, ctx: CanvasProviderCloseRequest) -> None:
        """Canvas was closed by the user or agent. Default: no-op."""

    async def on_action(self, ctx: CanvasProviderInvokeActionRequest) -> Any:
        """Handle a non-lifecycle action declared by the canvas."""
        raise CanvasError.no_handler()

View on GitHub (pinned to cd8cf15dc3)