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

  1. 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.
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/ee386e8b0edf9e29. Report an issue: GitHub.