{"record":{"id":"9c5090c11f4a7ebd","repo":"Zie619/n8n-workflows","slug":"the-json-is-not-an-array","errorCode":null,"errorMessage":"The JSON is not an array.","messagePattern":"The JSON is not an array\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"workflows/Http/1580_HTTP_Stickynote_Automate_Webhook.json","lineNumber":102,"sourceCode":"      \"credentials\": {\n        \"openRouterApi\": {\n          \"id\": \"WZWYWCfluxuKxZzV\",\n          \"name\": \"OpenRouter account\"\n        }\n      },\n      \"typeVersion\": 1,\n      \"notes\": \"This lmChatOpenRouter node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"4ab360eb-858f-48b8-a00d-71867d4f0c93\",\n      \"name\": \"Parse and Chunk JSON Data\",\n      \"type\": \"n8n-nodes-base.code\",\n      \"position\": [\n        -1420,\n        160\n      ],\n      \"parameters\": {\n        \"jsCode\": \"// Parse the input JSON string and split it into four chunks\\nconst rawText = $json.text;\\n\\n// Remove Markdown JSON code blocks if present\\nconst cleanedText = rawText.replace(/```json|```/g, '').trim();\\n\\ntry {\\n const jsonArray = JSON.parse(cleanedText);\\n if (!Array.isArray(jsonArray)) {\\n throw new Error('The JSON is not an array.');\\n }\\n const chunkSize = Math.ceil(jsonArray.length / 4);\\n const chunks = [];\\n for (let i = 0; i < jsonArray.length; i += chunkSize) {\\n chunks.push(jsonArray.slice(i, i + chunkSize));\\n }\\n return chunks.map(chunk => ({ json: { chunk } }));\\n} catch (error) {\\n return [{ json: { error: error.message } }];\\n}\\n\"\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This code node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"5a3ac393-8355-449f-93cb-b98e8bee9b80\",\n      \"name\": \"Perform SerpAPI Search Request\",\n      \"type\": \"n8n-nodes-base.httpRequest\",\n      \"position\": [\n        -780,\n        180\n      ],\n      \"parameters\": {\n        \"url\": \"{{ $env.API_BASE_URL }}\",\n        \"options\": {},\n        \"sendQuery\": true,\n        \"queryParameters\": {\n          \"parameters\": [","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/Zie619/n8n-workflows/blob/94007c1445d9258a7da116646b79473e7c7c3282/workflows/Http/1580_HTTP_Stickynote_Automate_Webhook.json#L84-L120","documentation":"Produced by 'Parse and Chunk JSON Data' (workflow 1580): it takes $json.text from an lmChatOpenRouter node, strips markdown fences, JSON.parses it, and throws 'The JSON is not an array.' when the parsed value is not an Array. The surrounding try/catch converts the throw into an output item {json:{error}}, so this message typically surfaces as data flowing into 'Perform SerpAPI Search Request', not as a failed execution.","triggerScenarios":"The OpenRouter LLM returns valid JSON that is an object (e.g. {\"results\": [...]} or {\"error\": ...}) or a bare string/number instead of a top-level array. Any parse syntax error would hit the same catch but report the parser's message; this exact string means parsing succeeded and Array.isArray failed.","commonSituations":"Prompt does not force array-only output and the model wraps results in a key; model substitution on OpenRouter changing output style; model prefixing an apology despite instructions; temperature/settings letting the model answer in prose that happens to parse (e.g. quoted string).","solutions":["Strengthen the LLM prompt: 'Respond with ONLY a raw JSON array, no wrapper object, no prose, no code fences.'","Unwrap common envelope shapes before the array check (see exampleFix).","Check the {error} output item downstream before using chunk — the SerpAPI node currently consumes it blindly as its q parameter.","Switch the OpenRouter node to a model/params (low temperature, json/response_format if supported) that yields deterministic JSON."],"exampleFix":"// before\nconst jsonArray = JSON.parse(cleanedText);\nif (!Array.isArray(jsonArray)) {\n  throw new Error('The JSON is not an array.');\n}\n\n// after\nlet jsonArray = JSON.parse(cleanedText);\nif (!Array.isArray(jsonArray)) {\n  // unwrap common LLM envelopes\n  const envelopeValues = Object.values(jsonArray).filter(Array.isArray);\n  if (envelopeValues.length === 1) jsonArray = envelopeValues[0];\n  else throw new Error('The JSON is not an array.');\n}","handlingStrategy":"validation","validationCode":"const cleanedText = String($json.text ?? '').replace(/```json|```/g, '').trim();\nlet parsed = JSON.parse(cleanedText); // may throw -> caught below\nif (!Array.isArray(parsed)) {\n  const arrays = Object.values(parsed ?? {}).filter(Array.isArray);\n  parsed = arrays.length === 1 ? arrays[0] : null;\n}\nif (!parsed) throw new Error('The JSON is not an array.');","typeGuard":"function isJsonArrayText(text) {\n  try {\n    return Array.isArray(JSON.parse(String(text).replace(/```json|```/g, '').trim()));\n  } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Prompt the model explicitly: 'output ONLY a raw JSON array, no wrapper object, no prose'.","Never let the {error} item flow into the SerpAPI request unchecked — branch on json.error first.","Prefer low temperature and structured-output settings for JSON-producing LLM calls."],"tags":["n8n","llm","openrouter","json-parsing","ai-output"],"backgroundTag":null,"analyzedSha":"94007c1445d9258a7da116646b79473e7c7c3282","analyzedAt":"2026-08-15T04:10:37.591Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}