n8n-io/n8n · error · NodeOperationError

Parameter ${parameter.name} is not a valid JSON: ${error.mes

Error message

Parameter ${parameter.name} is not a valid JSON: ${error.message}

What it means

Thrown by configureToolFunction when a tool parameter of type 'json' receives a string argument (not already an object) that fails JSON.parse. This is distinct from the raw (qsRaw/headersRaw/bodyRaw) placeholders; it applies to individually-typed json parameters.

Source

Thrown at packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/utils.ts:771

							`Model did not provide parameter '${parameter.name}' which is required and must be present in the input`,
							{ itemIndex },
						);
					}
				}

				for (const parameter of toolParameters) {
					let argument = dataFromModel[parameter.name];

					if (
						argument &&
						parameter.type === 'json' &&
						!['qsRaw', 'headersRaw', 'bodyRaw'].includes(parameter.key ?? '') &&
						typeof argument !== 'object'
					) {
						try {
							argument = jsonParse(String(argument));
						} catch (error) {
							throw new NodeOperationError(
								ctx.getNode(),
								`Parameter ${parameter.name} is not a valid JSON: ${error.message}`,
								{
									itemIndex,
								},
							);
						}
					}

					if (parameter.sendIn === 'path') {
						argument = String(argument);

						//remove " or ' from start or end
						argument = argument.replace(/^['"]+|['"]+$/g, '');

						options.url = options.url.replace(`{${parameter.name}}`, argument);
						continue;
					}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Clarify in the parameter description that the value must be a valid JSON object string.
  2. Change the parameter type to 'string' and parse it downstream in the sub-workflow.
  3. Simplify the JSON shape the model must produce.
  4. Use the structured-tool schema path so the framework coerces the value.

Example fix

// before: parameter type 'json', model sends '{ name: "x" }' (unquoted keys) → throws
// after: parameter type 'string', then parse in the workflow
{ name: 'filter', type: 'string' }
// in sub-workflow: JSON.parse($json.filter)
Defensive patterns

Strategy: try-catch

Validate before calling

if (parameter.type === 'json' && typeof argument === 'string') {
  try { argument = JSON.parse(argument); }
  catch { argument = { value: argument }; /* graceful wrap */ }
}

Type guard

function looksLikeJson(v: string): boolean {
  const t = v.trim(); return (t.startsWith('{') && t.endsWith('}')) || (t.startsWith('[') && t.endsWith(']'));
}

Try / catch

try { argument = jsonParse(String(argument)); }
catch (e) { /* degrade to string param or re-prompt */ }

Prevention

When it happens

Trigger: The model supplies a malformed JSON string for a parameter declared as type 'json' (e.g. unquoted keys, single quotes, trailing comma); the model sends a plain string where a JSON object was expected.

Common situations: Complex nested JSON parameters that confuse the model; models that wrap JSON in backticks or prose; type set to 'json' but the model treats it as free text.

Related errors


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