n8n-io/n8n · error · NodeOperationError

Could not replace placeholders in ${key}: ${error.message}

Error message

Could not replace placeholders in ${key}: ${error.message}

What it means

Thrown by configureToolFunction when parsing a raw request option block (qs, headers, or body entered as a JSON string with placeholders) and jsonParse with repairJSON:true still fails. The placeholder substitution relies on a parseable JSON skeleton, so an unrepairable skeleton aborts.

Source

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

								`{${parameter.name}}`,
								String(argument),
							);
						}

						set(options, [parameter.sendIn, parameter.key], requestOptionsValue);
						continue;
					}

					set(options, [parameter.sendIn, parameter.name], argument);
				}

				for (const [key, value] of Object.entries(clonedRawRequestOptions)) {
					if (value) {
						let parsedValue;
						try {
							parsedValue = jsonParse<IDataObject>(value, { repairJSON: true });
						} catch (error) {
							throw new NodeOperationError(
								ctx.getNode(),
								`Could not replace placeholders in ${key}: ${error.message}`,
							);
						}
						options[key as 'qs' | 'headers' | 'body'] = parsedValue;
					}
				}
			}

			if (options) {
				options.url = encodeURI(options.url);

				if (options.headers && !Object.keys(options.headers).length) {
					delete options.headers;
				}
				if (options.qs && !Object.keys(options.qs).length) {
					delete options.qs;
				}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Open the node's raw JSON input for the named key (the message tells you which: qs/headers/body) and paste it into a JSON linter; fix all syntax errors.
  2. Switch that input from 'JSON' mode to 'Keypair' mode so the UI guarantees valid structure.
  3. Remove the offending block and re-enter it field by field.
  4. Validate the JSON in an external tool before pasting back.

Example fix

// before (headers raw, broken):
{ "Authorization": "Bearer {token", "Accept": "application/json" }
// (missing closing brace in placeholder)

// after:
{ "Authorization": "Bearer {token}", "Accept": "application/json" }
Defensive patterns

Strategy: validation

Validate before calling

for (const [key, value] of Object.entries(rawRequestOptions)) {
  if (value) {
    try { JSON.parse(value); }
    catch { throw new Error(`Fix the raw JSON for '${key}' before running: ${value}`); }
  }
}

Type guard

function isValidJsonString(s: string): boolean {
  try { JSON.parse(s); return true; } catch { return false; }
}

Try / catch

try { parsedValue = jsonParse<IDataObject>(value, { repairJSON: true }); }
catch (e) { /* surface a clear 'fix raw JSON for key X' message */ }

Prevention

When it happens

Trigger: The user enters a raw JSON block in the node (Query / Headers / Body tab using 'JSON' input mode) with severe syntax errors — mismatched braces, stray characters, or unbalanced brackets that the repair heuristic cannot fix.

Common situations: Hand-editing the raw JSON and introducing a typo; copy-pasting from a source that mangled quotes; partial edits leaving dangling commas or unclosed objects.

Related errors


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