n8n-io/n8n · error · StaleResumeError
No tool call found for toolCallId: ${options.toolCallId}
Error message
No tool call found for toolCallId: ${options.toolCallId} What it means
Thrown when an HTTP Request node is configured with contentType='json' but the jsonBody actually looks like XML/SOAP. The validator flags this because sending an XML envelope through the JSON serializer produces a malformed request. Detected via payload starting with <?xml, <soap, <s:envelope, <env:envelope, or an expression referencing a soap/xml body field.
Source
Thrown at packages/@n8n/agents/src/runtime/loop/agent-runtime.ts:365
async resume(
method: 'stream',
data: unknown,
options: ResumeOptions & ExecutionOptions,
): Promise<StreamResult>;
async resume(
method: 'generate' | 'stream',
data: unknown,
options: ResumeOptions & ExecutionOptions,
): Promise<GenerateResult | StreamResult> {
this.runId = options.runId;
const state = await this.runState.resume(this.runId);
if (!state) {
throw new StaleResumeError(`No suspended run found for runId: ${this.runId}`);
}
const toolCall = state.pendingToolCalls[options.toolCallId];
if (!toolCall) {
throw new StaleResumeError(`No tool call found for toolCallId: ${options.toolCallId}`);
}
const list = AgentMessageList.deserialize(state.messageList);
this.context.hydrateDeferredToolsFromList(list);
await hydrateFileParts(list.messages(), this.config.fileStore, {
threadId: state.persistence?.threadId,
});
const tool = this.context
.getCurrentTools(state.persistence)
.find((t) => t.name === toolCall.toolName);
if (!tool) throw new Error(`Tool ${toolCall.toolName} not found`);
let resumeData: unknown = data;
let abortScope: AgentAbortScope | undefined;
const resumeSchema = toolCall.suspended ? toolCall.resumeSchema : tool.resumeSchema;
if (!isCancellation(resumeData) && resumeSchema) {View on GitHub (pinned to 5ac6606e81)
Solutions
- Set sendBody=true, contentType='raw', and place the envelope in the body field with rawContentType='text/xml' (or 'application/xml').
- Omit specifyBody, jsonBody, and bodyParameters so they do not conflict with the raw body.
- If the payload is dynamic, put an expression in body such as expr('{{ $json.soapBody }}') while keeping contentType='raw' and an XML rawContentType.
Example fix
// before
httpRequest({
name: 'SOAP Call',
sendBody: true,
contentType: 'json',
jsonBody: '<soap:Envelope ...></soap:Envelope>',
});
// after
httpRequest({
name: 'SOAP Call',
sendBody: true,
contentType: 'raw',
rawContentType: 'text/xml',
body: '<soap:Envelope ...></soap:Envelope>',
}); Defensive patterns
Strategy: validation
Validate before calling
const XML_START = /^\s*(?:=\s*)?(?:\{{\s*)?['"`]?\s*(?:<\?xml|<soap:?|<s:envelope|<env:envelope)/i;
function looksLikeXml(value: unknown): boolean {
return typeof value === 'string' && XML_START.test(value);
}
// before building:
if (params.contentType === 'json' && looksLikeXml(params.jsonBody)) {
throw new Error('XML payload detected; use contentType=raw with rawContentType=text/xml and the body field.');
} Prevention
- When integrating SOAP/XML services, default to contentType='raw' with an XML rawContentType.
- Never put an XML envelope in jsonBody; keep JSON bodies strictly JSON.
- In an AI workflow generator, branch on detected payload type before assigning contentType.
When it happens
Trigger: params.contentType === 'json' AND looksLikeXmlPayload(params.jsonBody) is true — i.e. the jsonBody string matches the XML start pattern, or it is an expression (starts with '=' / contains '{{') whose body matches a soap/xml body/payload/envelope reference.
Common situations: Integrating a SOAP/legacy service and leaving the default JSON content type; an AI builder copies an XML envelope into jsonBody; the provider's docs show a SOAP envelope and the model maps it to the JSON body field.
Related errors
- Tool ${toolCall.toolName} not found
- LangSmithTelemetry creates its own tracer — do not use .otlp
- No suspended run found for runId: ${this.runId}
- Invalid resume payload: ${parseResult.error}
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/0b518dfffe7f71d6.
Report an issue: GitHub.