n8n Error Reference
Invalid JSON / SyntaxError in JSON
This error fires when n8n tries to parse a value as JSON and the value is not valid JSON. The position number in the error message points to the exact character that broke the parser — which usually reveals whether the input is HTML, markdown, a truncated string, or structurally malformed.
Quick Diagnosis
Find the raw value that failed to parse — not the error message, the actual string n8n tried to parse. Copy it and run JSON.parse(...) in a browser console or JSON validator. If it starts with < — the node received HTML, not JSON. If it starts with a backtick — an AI node returned markdown. If it's empty — the upstream node returned nothing.
Error variants
The position number in parentheses is the character offset where the parser failed — position 0 means the very first character is wrong, which almost always means HTML or markdown instead of JSON:
What this error means
JSON has strict syntax rules: double-quoted keys, double-quoted string values, no trailing commas, no comments, no single quotes, no undefined. When n8n calls JSON.parse() on a value that violates any of these rules, it throws a SyntaxError. The position number in the message is the character offset where the parser gave up — position 0 means the very first character is wrong (almost always HTML or markdown), while a high position number points to a structural problem deep inside an otherwise valid-looking value. The error itself is not the diagnosis — the raw string that failed to parse is.
Common causes
HTTP Request node received HTML instead of JSON
The remote server returned an HTML error page, a login redirect, or a maintenance page instead of the expected JSON response. This is the most common cause of "Unexpected token < at position 0." The HTTP Request node does not validate Content-Type — it passes whatever the server returned to the next node.
AI or OpenAI node wrapped the response in markdown
Language models default to markdown-formatted output. Without explicit JSON mode or a strict system prompt instruction, an OpenAI or AI node will wrap JSON in ```json ... ``` fences. When a downstream node tries to parse the fenced string as JSON, the backtick at position 0 breaks the parser immediately.
Code node returning a JSON string instead of the required array shape
n8n Code nodes must return [{ json: { ... } }]. If the code calls JSON.stringify() and returns the resulting string as the node's output, or returns a plain object instead of the wrapped array, downstream nodes receive a string or malformed structure — and any JSON.parse() call on it fails.
Expression interpolation inserting unescaped characters
A Set/Edit Fields or HTTP Request node uses an expression like `{"key": "{{ $json.userInput }}"}` to construct a JSON body. If userInput contains double quotes, newlines, or backslashes, those characters break the JSON string without escaping. The resulting value is syntactically invalid at the character where the injected content starts.
Webhook body parsed as plain text
The service sending data to the Webhook node did not set Content-Type: application/json. n8n's Webhook node reads the Content-Type header to decide whether to auto-parse the body. Without it, the body arrives as a raw string in $json.body — and any downstream node expecting a parsed object receives a string instead.
Empty or truncated response from upstream
A 204 No Content response, a network timeout that returned partial data, or a Code node that conditionally returns nothing on some branches produces an empty or incomplete string. JSON.parse() throws "Unexpected end of JSON input" on any empty or unterminated input.
How to diagnose it
- 1
Open the failed execution, click the failed node, and go to the Input tab. Find the raw value that was being parsed — not the error message, the actual string or field that triggered the SyntaxError.
- 2
Copy that raw value and paste it into a JSON validator (browser console: JSON.parse('...'), or any online validator). The validator will show the exact position and character that broke parsing.
- 3
Check the first character of the failing value. If it starts with < — the node received HTML or XML, not JSON. If it starts with a backtick or the word "json" — an AI node returned markdown-fenced output. If it starts with { or [ — the structure itself is malformed.
- 4
For HTTP Request nodes: open the node, go to the Response tab in the execution output, and check the Content-Type header. text/html or text/plain confirms the server did not return JSON.
- 5
For AI and OpenAI nodes: check whether JSON mode is enabled in the node settings. If not, open the system prompt and confirm it explicitly instructs the model to return raw JSON with no markdown fences.
- 6
For Code nodes: log the return value before the node exits — add console.log(JSON.stringify(returnValue)) and re-run. Confirm the shape is [{ json: { ... } }] and the value is a JavaScript object, not a string.
- 7
For Set/Edit Fields or HTTP Request body expressions: check every expression that injects dynamic data into a JSON string. Replace string-concatenation expressions with structured field mappings, or use $json.fieldName directly in the field value rather than inside a JSON string template.
n8n-specific scenarios
HTTP Request node → Unexpected token '<' at position 0
The endpoint returned an HTML page instead of JSON. Common triggers: the URL is wrong (hitting a docs or login page), the API key is missing so the server returned a 401 HTML error page, or the endpoint redirected to a page that doesn't return JSON. Open the HTTP Request node, enable "Include Response Headers" in Options, re-run, and check the Content-Type and status code in the output. Fix the URL, add the missing auth header, or contact the API provider if the endpoint behavior changed.
OpenAI or AI node → markdown fences breaking downstream parse
The model returned ```json\n{...}\n``` instead of raw JSON. Two fixes: (1) Enable JSON mode in the node settings — this forces the model to return a raw JSON object. (2) If JSON mode is unavailable, add to the system prompt: 'Return only a raw JSON object. Do not use markdown formatting, code fences, or any text before or after the JSON.' Even with both in place, test with the actual prompt — some models ignore the instruction for complex outputs.
Code node → returning a string instead of the required array
Two common mistakes: calling return JSON.stringify(result) when n8n already expects a JavaScript object, and returning a plain object like return { key: value } instead of [{ json: { key: value } }]. Both cause downstream nodes to receive either a string or a structurally wrong shape. The fix: always return an array of objects with a json key — return [{ json: result }] — and never call JSON.stringify() on the return value itself.
Webhook node → body arriving as a plain string
The service posting to the Webhook did not send Content-Type: application/json. The body is accessible but as a raw string in $json.body rather than a parsed object. Fix on the sender side if you control it. If you don't, add a Code node immediately after the Webhook that calls JSON.parse($json.body) and returns the parsed object — but wrap it in try/catch to handle cases where the body is empty or malformed.
Set/Edit Fields node → expression injecting unescaped user data into JSON
An expression like `{"message": "{{ $json.text }}"}` in the Value field of a Set/Edit Fields node will break if $json.text contains a double quote, newline, or backslash. Instead of constructing a JSON string in an expression, set the field type to Object and map $json.text directly as a field value — n8n handles escaping automatically.
Edit Fields / downstream node → 'Unexpected end of JSON input' on empty branch
A Filter or If node upstream routed execution down a branch that matched zero items. The downstream node still ran but received an empty input. When it tried to parse $json.body or call JSON.parse() on an empty string, it threw "Unexpected end of JSON input." Add an If node before any JSON.parse() call to confirm the input is non-empty: {{ $json.body !== undefined && $json.body !== '' }}.
Paste your full error message and the failed node's output into Debugalo to get a structured breakdown: the exact failed step, root cause, and concrete fix steps.
Analyze this error →FAQ
What does 'Unexpected token < at position 0' mean?
The value n8n tried to parse as JSON starts with <, which is the first character of an HTML tag. The node received an HTML page — a login page, error page, or redirect — instead of a JSON response. Position 0 means the very first character is wrong, so nothing in the response is valid JSON. Fix the upstream HTTP Request node: check the URL, authentication headers, and the Content-Type of the server's response.
How do I make an OpenAI or AI node return raw JSON instead of markdown?
First, enable JSON mode in the node settings if available — this forces a raw JSON object with no wrapping. Second, add an explicit instruction to the system prompt: 'Return only a raw JSON object. Do not use code fences, markdown formatting, or any text outside the JSON.' If you are using an HTTP Request node to call the API directly, set response_format to { "type": "json_object" } in the request body.
My Code node returns valid JSON — why does the next node fail?
The issue is almost always the return shape, not the JSON validity. n8n Code nodes require return [{ json: { ... } }] — an array of objects each with a json key. Returning a plain object, a JSON string, or a nested array without the json wrapper causes downstream nodes to receive malformed input. Add console.log(JSON.stringify(returnValue)) before the return statement and check the execution log to confirm the shape.
How do I safely parse a JSON string inside a Code node?
Wrap JSON.parse() in a try/catch block: try { const parsed = JSON.parse(inputString); return [{ json: parsed }]; } catch (e) { return [{ json: { error: e.message, raw: inputString } }]; }. The catch branch lets you inspect the raw string that failed and route it to a Stop And Error node or a fallback branch instead of crashing the whole execution.
The Webhook body looks like valid JSON in the logs — why does parsing still fail?
Check whether n8n auto-parsed the body or left it as a string. If the sender did not include Content-Type: application/json, n8n receives the body as a raw string. In the Webhook node output, if $json.body is a string value (shown with quotes around it in the Output tab), it was not auto-parsed. Add a Code node after the Webhook: return [{ json: JSON.parse($input.first().json.body) }].