n8n-io/n8n · error · Error

No cancellation handler is available for "${request.subAgent

Error message

No cancellation handler is available for "${request.subAgentId}".

What it means

Thrown from the inline sub-agent wiring inside Agent when the delegate-subagent tool's cancelSubAgent handler receives a cancel request it cannot route. Routing tries the inline sub-agent first (INLINE_SUB_AGENT_ID), then delegates to the host-supplied hostCancelRunner; if neither applies, there is no way to cancel that sub-agent id. The error identifies the unroutable subAgentId verbatim.

Source

Thrown at packages/@n8n/agents/src/sdk/agent.ts:1201

								}
								if (hostResumeRunner !== undefined) {
									return await hostResumeRunner(request, helpersFromHandler);
								}
								return configuredSubAgentNotFound(request);
							},
							cancelSubAgent: async (
								request: DelegateSubAgentCancelRequest,
								helpersFromHandler: DelegateSubAgentRunnerHelpers,
							) => {
								if (request.subAgentId === INLINE_SUB_AGENT_ID) {
									await options.runState.cancel(request.childRunId);
									return;
								}
								if (hostCancelRunner !== undefined) {
									await hostCancelRunner(request, helpersFromHandler);
									return;
								}
								throw new Error(
									`No cancellation handler is available for "${request.subAgentId}".`,
								);
							},
						}
					: {}),
			});

			if (tool.approval?.required === true) {
				return wrapToolForApproval(completedTool, { requireApproval: true });
			}
			return completedTool;
		});
	}

	private createInlineSubAgentRunner(options: {
		deferredTools: BuiltTool[];
		modelConfig: ModelConfig;
		runState: RunStateManager;

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Supply a cancelSubAgent handler in the delegate sub-agent options alongside resumeSubAgent so host-managed sub-agents can be cancelled.
  2. Ensure every subAgentId the runtime can emit is either 'inline' or covered by your host runner.
  3. If you do not need suspend/resume, do not set resumeSubAgent so childCanSuspend stays false and the cancel handler is never registered.

Example fix

// before
createDelegateSubAgentTool({
  runSubAgent: hostRun,
  resumeSubAgent: hostResume,
  // cancelSubAgent missing
});
// after
createDelegateSubAgentTool({
  runSubAgent: hostRun,
  resumeSubAgent: hostResume,
  cancelSubAgent: async (req, helpers) => { await hostCancel(req, helpers); },
});
Defensive patterns

Strategy: validation

Validate before calling

import { INLINE_SUB_AGENT_ID } from '@n8n/agents';

function assertCancelRoutable(subAgentId: string, hostCancelRunner?: Function) {
  if (subAgentId === INLINE_SUB_AGENT_ID) return;
  if (typeof hostCancelRunner !== 'function') {
    throw new Error(`No host cancelSubAgent for non-inline subAgentId "${subAgentId}"`);
  }
}

Type guard

function isInlineSubAgentId(id: string): boolean {
  return id === 'inline';
}

Try / catch

try {
  await delegate.cancel(request);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('No cancellation handler')) {
    // log and degrade: mark the run as un-cancellable, surface to user
  } else throw e;
}

Prevention

When it happens

Trigger: A host constructs a delegate sub-agent tool via createDelegateSubAgentTool (or supplies delegateOptions) WITHOUT setting cancelSubAgent, then the runtime issues a DelegateSubAgentCancelRequest whose subAgentId is something other than 'inline'. Because the cancel handler is only registered when childCanSuspend is true, this surfaces during a suspend/resume lifecycle of a host-managed sub-agent.

Common situations: Custom host integrations that wire resumeSubAgent but forget cancelSubAgent; upgrading @n8n/agents and relying on prior behavior where cancellation was a no-op; passing a non-inline subAgentId from a checkpoint-resume path that the host runner does not recognize.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/35a46f28ecc6ea15. Report an issue: GitHub.