n8n-io/n8n · warning · Error

Invalid resume payload: ${parseResult.error}

Error message

Invalid resume payload: ${parseResult.error}

What it means

Thrown when a query parameter whose name looks credential-like (matches api_key, access_token, auth_token, bearer_token, secret_key, private_key, client_secret, password, credentials, or exactly token/secret/auth) is set to a hardcoded literal value. The validator wants query-string secrets routed through the credential system, typically httpQueryAuth.

Source

Thrown at packages/@n8n/agents/src/runtime/loop/agent-runtime.ts:386

		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) {
			const parseResult = await parseWithSchema(resumeSchema, data, { stripUnknown: true });
			if (!parseResult.success) {
				throw new Error(`Invalid resume payload: ${parseResult.error}`);
			}
			resumeData = parseResult.data as JSONValue;
		}

		try {
			// Merge persisted execution options with fresh caller options
			const {
				runId: _rid,
				toolCallId: _tcid,
				onResumeClaimed: _onResumeClaimed,
				...callerExecOptions
			} = options;
			const persisted = state.executionOptions ?? {};
			const persistedMaxIterations = persisted.maxIterations;
			const callerMaxIterations = callerExecOptions.maxIterations;
			if (
				callerMaxIterations !== undefined &&
				persistedMaxIterations !== undefined &&

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Create an httpQueryAuth credential and reference it from the node (authentication='genericCredentialType', genericAuthType='httpQueryAuth').
  2. If reusing templated auth, use httpTemplatedCustomAuth with a {"query":{...}} template.
  3. Wrap the value in an expression so it is not flagged as a hardcoded literal, though a credential is strongly preferred for any persisted secret.

Example fix

// before
httpRequest({
  name: 'Search',
  queryParameters: { parameters: [{ name: 'api_key', value: 'live_key_xyz' }] },
});

// after
httpRequest({
  name: 'Search',
  authentication: 'genericCredentialType',
  genericAuthType: 'httpQueryAuth',
  credentials: { httpQueryAuth: { id: 'EXISTING_CRED_ID' } },
});
Defensive patterns

Strategy: validation

Validate before calling

import { isCredentialFieldName, containsExpression } from './validation-helpers';

function findHardcodedCredQueryParams(queryParameters: { parameters?: Array<{ name?: string; value?: unknown }> } | undefined): string[] {
  const offenders: string[] = [];
  for (const p of queryParameters?.parameters ?? []) {
    const valueStr = typeof p.value === 'string' ? p.value : JSON.stringify(p.value);
    if (p.name && isCredentialFieldName(p.name) && p.value && !containsExpression(valueStr)) {
      offenders.push(p.name);
    }
  }
  return offenders;
}

Prevention

When it happens

Trigger: A queryParameters.parameters[] entry where isCredentialFieldName(param.name) is true, param.value is truthy, and the value string does not start with '=' and does not contain '={{'.

Common situations: Pasting '?api_key=...' from vendor docs into queryParameters; an AI builder inlining the token in the URL; testing with a live key left as a literal.

Related errors


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