n8n-io/n8n · error · Error
Reflector field "${fieldName}" must be an array
Error message
Reflector field "${fieldName}" must be an array What it means
The observation-log reflector asks an LLM to return a JSON object with `drop` (string[]) and `merge` (array) fields. `readStringArray` validates that a field the reflector contract requires to be a string array is actually an array. This fires when the LLM emits a non-array value (object, string, number, null) for a field the parser expects to be `string[]`.
Source
Thrown at packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts:288
parentId: string | null | undefined,
activeById: Map<string, ObservationLogEntry>,
removedIds: Set<string>,
): string | null | undefined {
if (parentId === undefined || parentId === null) return parentId;
return activeById.has(parentId) && !removedIds.has(parentId) ? parentId : null;
}
function extractJsonObject(output: string): string {
const start = output.indexOf('{');
const end = output.lastIndexOf('}');
if (start === -1 || end === -1 || end < start) {
throw new Error('Reflector output did not contain a JSON object');
}
return output.slice(start, end + 1);
}
function readStringArray(value: unknown, fieldName: string): string[] {
if (!Array.isArray(value)) throw new Error(`Reflector field "${fieldName}" must be an array`);
const strings: string[] = [];
for (const item of value) {
if (typeof item !== 'string') {
throw new Error(`Reflector field "${fieldName}" must contain only strings`);
}
strings.push(item);
}
return strings;
}
function readMergeArray(value: unknown): ObservationLogMerge[] {
if (!Array.isArray(value)) throw new Error('Reflector field "merge" must be an array');
return value.map(readMerge);
}
function readMerge(value: unknown, index: number): ObservationLogMerge {
if (!isRecord(value)) throw new Error(`Reflector merge[${index}] must be an object`);
const supersedes = readStringArray(value.supersedes, `merge[${index}].supersedes`);View on GitHub (pinned to 5ac6606e81)
Solutions
- Inspect the raw reflector LLM `output` string (log it before parse) to see what the model actually returned for the field named in the error.
- Strengthen the reflector system prompt to reiterate that `drop` and `merge[i].supersedes` must be JSON arrays of strings, not comma-delimited strings or objects.
- If using a weaker model, switch to one with stronger structured-output / JSON-mode support, or enable the provider's JSON response format if available.
- As a defensive measure, pre-normalize known-shape violations (e.g. split comma strings into arrays) before calling `parseObservationLogReflectionJson`, though fixing the prompt is preferred.
Example fix
// before: LLM returns { "drop": "obs-1,obs-2" }
// Strengthen the reflector prompt:
/*
You MUST return JSON matching exactly this shape:
{
"drop": ["id1", "id2"], // array of strings, NEVER a comma-separated string
"merge": [...]
}
*/
// after: LLM returns { "drop": ["obs-1", "obs-2"] } Defensive patterns
Strategy: validation
Validate before calling
// Validate reflector LLM output shape before parsing
function isValidReflectionShape(raw: unknown): raw is { drop: unknown[]; merge: unknown[] } {
return typeof raw === 'object' && raw !== null &&
Array.isArray((raw as any).drop) && Array.isArray((raw as any).merge);
}
// Usage:
const json = JSON.parse(extractJsonObject(output));
if (!isValidReflectionShape(json)) {
throw new Error('Reflector output shape invalid: drop and merge must be arrays');
} Type guard
function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every((item) => typeof item === 'string');
} Try / catch
try {
const reflection = parseObservationLogReflectionJson(output);
} catch (e) {
// Reflector LLM produced malformed JSON — log raw output and skip this reflection cycle
logger.warn('Reflection parse failed, skipping cycle', { output, error: (e as Error).message });
} Prevention
- Use a provider that supports structured-output / JSON-schema-constrained decoding for the reflector LLM call.
- Log the raw reflector output string on every parse failure for debugging.
- Treat reflection as best-effort: catch parse errors and skip the cycle rather than crashing the agent run.
- Pin the reflector model to one known to follow JSON schemas reliably.
When it happens
Trigger: The LLM-driven `reflect()` call returns JSON where a field expected to be `string[]` (e.g. `drop`, or `merge[i].supersedes`) is not an array — for example `{ "drop": "id1,id2" }` (a comma-separated string instead of an array) or `{ "drop": null }`.
Common situations: The reflector prompt was modified or weakened so the model stops emitting arrays. A cheaper/different model that follows the JSON schema less reliably was swapped in. The LLM wrapped the array in an extra object layer.
Related errors
- Reflector field "merge" must be an array
- Reflector field "${fieldName}" must contain only strings
- Reflector merge[${index}] must be an object
- Reflector merge[${index}].text must be a string
- Reflector merge[${index}].marker must be a known observation
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/ee386e8b0edf9e29.
Report an issue: GitHub.