toeverything/AFFiNE · error · BlockSuiteError

ExecutionError

ExecutionError

Error message

Error occurred while executing ${evtName} handler of tool "${tool?.toolName}"

What it means

invokeToolHandler catches any exception thrown inside a gfx tool's event handler (e.g. pointer down/move) and rewraps it as a fatal ExecutionError naming the event and tool, preserving the original as cause so tool bugs are attributable.

Source

Thrown at blocksuite/framework/std/src/gfx/tool/tool-controller.ts:287

      evt: PointerEventState,
      tool?: BaseTool
    ) => {
      const evtHooks = hooks[evtName];
      const stopHandler = evtHooks?.reduce((pre, hook) => {
        return pre || hook(evt) === false;
      }, false);

      tool = tool ?? this.currentTool$.peek();

      if (stopHandler) {
        return false;
      }

      try {
        tool?.[evtName](evt);
        return true;
      } catch (e) {
        throw new BlockSuiteError(
          ErrorCode.ExecutionError,
          `Error occurred while executing ${evtName} handler of tool "${tool?.toolName}"`,
          {
            cause: e as Error,
          }
        );
      }
    };

    /**
     * Hook into the event lifecycle.
     * All hooks will be executed despite the current active tool.
     * This is useful for tools that need to perform some action before an event is handled.
     * @param evtName
     * @param handler
     */
    const addHook: ToolEventTarget['addHook'] = (evtName, handler) => {
      hooks[evtName] = hooks[evtName] ?? [];

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Check the inner error (usually attached as cause or logged just before) for the real failure inside the tool handler.
  2. Ensure the tool's handler for the event (pointerdown, drag, etc.) validates its inputs — null targets or missing models are the usual cause.
  3. If a custom tool throws, wrap its handlers and rethrow with context so the failing tool name is preserved.

Example fix

class MyTool extends BaseTool {
  override pointerdown(e: PointerEventState) {
    const target = this.gfx.getElementByPoint(e.point);
    if (!target) return; // guard instead of throwing
    // ...
  }
}
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at blocksuite/framework/std/src/gfx/tool/tool-controller.ts:287 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/4a30e4219e0f9b59. Report an issue: GitHub.