{"record":{"id":"2a85ec3539976836","repo":"n8n-io/n8n","slug":"reflector-merge-index-must-be-an-object","errorCode":null,"errorMessage":"Reflector merge[${index}] must be an object","messagePattern":"Reflector merge\\[(.+?)\\] must be an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts","lineNumber":305,"sourceCode":"function 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};\n}\n\nfunction readMarker(value: unknown, index: number): ObservationLogMarker {\n\tif (typeof value !== 'string') {\n\t\tthrow new Error(`Reflector merge[${index}].marker must be a known observation marker`);","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts#L287-L323","documentation":"Each element of the `merge` array must be a JSON object (record) with `supersedes`, `marker`, `text`, and optional `parentId` fields. `readMerge` calls `isRecord(value)` and throws if the element is not a plain object — for example a bare string, number, or null. The `index` in the message tells you which array position is malformed.","triggerScenarios":"The reflector LLM returns `{ \"merge\": [\"just text\", { ... }] }` where `merge[0]` is a string instead of an object, or `{ \"merge\": [null] }`.","commonSituations":"The model produced a mixed array of strings and objects. The model used `null` as a placeholder for a merge it could not produce. A truncated response cut off mid-object, leaving a bare value.","solutions":["Use the `index` from the error message to locate the offending element in the raw LLM output.","Update the reflector prompt: each `merge` element must be a complete object with `supersedes`, `marker`, and `text`; never emit bare text or null.","If truncation is the cause, increase the model's `maxOutputTokens` or reduce the observation-log input size so the reflector has budget to finish.","Enable structured-output / JSON mode on the provider to enforce the object shape at the API level."],"exampleFix":"// before: { \"merge\": [ \"replace obs-1\", { \"supersedes\": [...], ... } ] }\n// after:  { \"merge\": [ { \"supersedes\": [\"obs-1\"], \"marker\": \"INFO\", \"text\": \"replace obs-1\" } ] }","handlingStrategy":"type-guard","validationCode":"import { isRecord } from '@n8n/utils/is-record';\n\nconst raw = JSON.parse(extractJsonObject(output));\nif (Array.isArray(raw.merge)) {\n  raw.merge = raw.merge.filter(isRecord); // drop non-object elements\n}","typeGuard":"import { isRecord } from '@n8n/utils/is-record';\n\nfunction isMergeObject(value: unknown): value is Record<string, unknown> {\n  return isRecord(value);\n}","tryCatchPattern":"try {\n  const reflection = parseObservationLogReflectionJson(output);\n} catch (e) {\n  logger.warn('Reflector merge element not an object', { index: 'see message', output });\n}","preventionTips":["Filter non-object elements from the merge array before parsing.","Use structured-output / JSON-schema mode with `merge: array<object>`.","Ensure the reflector prompt shows complete merge objects in examples."],"tags":["llm-output-validation","observation-log","json-parsing","type-mismatch"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}