{"record":{"id":"88b1367adbee3325","repo":"n8n-io/n8n","slug":"reflector-field-merge-must-be-an-array","errorCode":null,"errorMessage":"Reflector field \"merge\" must be an array","messagePattern":"Reflector field \"merge\" must be an array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts","lineNumber":300,"sourceCode":"\t\tthrow new Error('Reflector output did not contain a JSON object');\n\t}\n\treturn output.slice(start, end + 1);\n}\n\nfunction readStringArray(value: unknown, fieldName: string): string[] {\n\tif (!Array.isArray(value)) throw new Error(`Reflector field \"${fieldName}\" must be an array`);\n\tconst strings: string[] = [];\n\tfor (const item of value) {\n\t\tif (typeof item !== 'string') {\n\t\t\tthrow new Error(`Reflector field \"${fieldName}\" must contain only strings`);\n\t\t}\n\t\tstrings.push(item);\n\t}\n\treturn strings;\n}\n\nfunction readMergeArray(value: unknown): ObservationLogMerge[] {\n\tif (!Array.isArray(value)) throw new Error('Reflector field \"merge\" must be an array');\n\treturn value.map(readMerge);\n}\n\nfunction readMerge(value: unknown, index: number): ObservationLogMerge {\n\tif (!isRecord(value)) throw new Error(`Reflector merge[${index}] must be an object`);\n\tconst supersedes = readStringArray(value.supersedes, `merge[${index}].supersedes`);\n\tconst marker = readMarker(value.marker, index);\n\tif (typeof value.text !== 'string') {\n\t\tthrow new Error(`Reflector merge[${index}].text must be a string`);\n\t}\n\n\tconst parentId = readOptionalParentId(value.parentId, index);\n\treturn {\n\t\tsupersedes,\n\t\tmarker,\n\t\ttext: value.text,\n\t\t...(parentId !== undefined && { parentId }),\n\t};","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts#L282-L318","documentation":"The reflector JSON contract requires a top-level `merge` field that is an array of merge-instructions. `readMergeArray` throws this when `parsed.merge` is present but not an array (e.g. an object or a string). Note that `parseObservationLogReflectionJson` defaults `merge` to `[]` when absent, so this only fires when the field exists but has the wrong type.","triggerScenarios":"The reflector LLM returns `{ \"merge\": { \"supersedes\": [], \"marker\": \"INFO\", \"text\": \"...\" } }` (a single object instead of an array) or `{ \"merge\": \"\" }`.","commonSituations":"The model emitted a single merge object rather than wrapping it in an array. The prompt example showed merge as an object, causing the model to mirror that shape. A model with poor array-handling collapsed the array.","solutions":["Log the raw reflector output to confirm `merge` is a non-array type.","Update the reflector system prompt to show `merge` as an array with at least one example element, emphasizing the brackets.","If the model consistently returns a single object, add a pre-parse normalization step that wraps a lone object into `[object]` before validation.","Switch to a model or mode that supports structured/JSON-schema-constrained output."],"exampleFix":"// before: { \"drop\": [], \"merge\": { \"supersedes\": [\"obs-1\"], \"marker\": \"INFO\", \"text\": \"...\" } }\n// after:  { \"drop\": [], \"merge\": [ { \"supersedes\": [\"obs-1\"], \"marker\": \"INFO\", \"text\": \"...\" } ] }","handlingStrategy":"validation","validationCode":"const raw = JSON.parse(extractJsonObject(output));\nif (raw.merge !== undefined && !Array.isArray(raw.merge)) {\n  // Normalize: wrap single object in array\n  if (typeof raw.merge === 'object' && raw.merge !== null) {\n    raw.merge = [raw.merge];\n  } else {\n    raw.merge = [];\n  }\n}","typeGuard":"function isMergeArray(value: unknown): value is unknown[] {\n  return Array.isArray(value);\n}","tryCatchPattern":"try {\n  const reflection = parseObservationLogReflectionJson(output);\n} catch (e) {\n  logger.warn('Reflector merge field not an array', { output });\n}","preventionTips":["Show the reflector prompt `merge` as `[...]` with explicit brackets in every example.","Pre-normalize the parsed JSON: if `merge` is a single object, wrap it in an array.","Enable structured-output mode to enforce `merge: array` at the API level."],"tags":["llm-output-validation","observation-log","json-parsing","reflection"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}