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
- Override on_action in your CanvasProvider subclass and implement the declared actions
- Route on ctx.request/ctx.action to the correct handler and return a JSON-serializable result
- Remove any declared actions your provider does not implement
- 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
- Always override on_action when your canvas declares any action
- Keep the canvas manifest and provider action handlers in sync (test one against the other)
- Deploy provider and canvas definition changes together
- Return an explicit 'unsupported action' result for unrecognized actions instead of relying on the base stub
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
- -32603
- Invalid entry '*': there is no bare wildcard. Use one or…
- Client is not connected. Call start() first.
- telemetry is not supported with…
- Set environment variables via either the client-level env…
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)