{"record":{"id":"a9511529f9ee8c73","repo":"n8n-io/n8n","slug":"sub-agent-tool-requires-confirmation-but-no-hitl-h","errorCode":null,"errorMessage":"Sub-agent tool requires confirmation but no HITL handler is available","messagePattern":"Sub-agent tool requires confirmation but no HITL handler is available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/src/stream/consume-with-hitl.ts","lineNumber":82,"sourceCode":"\t\t\t: result.status === 'errored'\n\t\t\t\t? 'failed while streaming'\n\t\t\t\t: `ended with unexpected status \"${result.status}\"`;\n\tthrow new Error(`${agentLabel} ${reason}`);\n}\n\n/**\n * Consume a sub-agent stream with HITL suspend/resume support.\n * Detects `tool-call-suspended` chunks, waits for user confirmation,\n * and resumes the stream. Used by delegate, builder, and other agent tools.\n *\n * Returns `{ text }` — a promise for the agent's full text output.\n * When HITL occurred, this returns the resumed stream's text (not the original).\n */\nexport async function consumeStreamWithHitl(\n\toptions: ConsumeWithHitlOptions,\n): Promise<ConsumeWithHitlResult> {\n\tif (!options.waitForConfirmation) {\n\t\tthrow new Error('Sub-agent tool requires confirmation but no HITL handler is available');\n\t}\n\n\tconst stream = normalizeStreamSource(options.stream);\n\tconst result = await executeResumableStream({\n\t\tagent: options.agent,\n\t\tstream,\n\t\tcontext: {\n\t\t\tthreadId: options.threadId,\n\t\t\trunId: options.runId,\n\t\t\tagentId: options.agentId,\n\t\t\teventBus: options.eventBus,\n\t\t\tsignal: options.abortSignal,\n\t\t\tlogger: options.logger,\n\t\t\toutputRedaction: options.outputRedaction,\n\t\t},\n\t\tcontrol: {\n\t\t\tmode: 'auto',\n\t\t\twaitForConfirmation: options.waitForConfirmation,","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/src/stream/consume-with-hitl.ts#L64-L100","documentation":"Thrown by consumeStreamWithHitl (consume-with-hitl.ts:81-83) when options.waitForConfirmation is not provided. Because the function's contract is to suspend on tool-call-suspended chunks and resume after confirmation, a missing handler means suspension cannot be satisfied — the guard fails fast before any streaming begins rather than deadlocking mid-stream.","triggerScenarios":"consumeStreamWithHitl is invoked without a waitForConfirmation callback. This is a programming error in the caller (the tool wiring), not a runtime data condition. The check runs synchronously on entry.","commonSituations":"A new sub-agent tool forgot to thread the HITL handler through; a test stub omitted waitForConfirmation; a non-HITL call site mistakenly used consumeStreamWithHitl instead of a plain consume helper.","solutions":["Pass a waitForConfirmation callback in ConsumeWithHitlOptions when calling consumeStreamWithHitl.","If the call site never needs HITL, use the non-HITL consume path instead.","In tests, provide a mock waitForConfirmation even if it is not exercised.","Audit the tool wiring to confirm the handler is threaded from the orchestrator context."],"exampleFix":"// before: consumeStreamWithHitl({ agent, stream, runId, agentId, eventBus, logger, threadId, abortSignal })\n// after:  consumeStreamWithHitl({ agent, stream, runId, agentId, eventBus, logger, threadId, abortSignal, waitForConfirmation })","handlingStrategy":"type-guard","validationCode":"function hasHitlHandler(opts: { waitForConfirmation?: unknown }): boolean {\n  return typeof opts.waitForConfirmation === 'function';\n}\nif (!hasHitlHandler(options)) throw new Error('consumeStreamWithHitl requires waitForConfirmation');","typeGuard":"function hasWaitForConfirmation(o: unknown): o is { waitForConfirmation: (id: string) => Promise<Record<string, unknown>> } {\n  return typeof (o as { waitForConfirmation?: unknown })?.waitForConfirmation === 'function';\n}","tryCatchPattern":"try { return await consumeStreamWithHitl(options); }\ncatch (e) {\n  if (e instanceof Error && e.message === 'Sub-agent tool requires confirmation but no HITL handler is available') {\n    // this is a programming error; fix the call site, do not retry as-is\n  }\n  throw e;\n}","preventionTips":["Always thread waitForConfirmation from the orchestrator context into consumeStreamWithHitl.","If HITL is never needed, use the non-HITL consume path instead.","Provide a mock handler in tests even when suspension is not exercised.","Add a type-check on options.waitForConfirmation at the call site."],"tags":["hitl","configuration","programming-error","sub-agent","guard"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}