n8n-io/n8n · error · Error

Reflector field "${fieldName}" must contain only strings

Error message

Reflector field "${fieldName}" must contain only strings

What it means

After `readStringArray` confirms a value is an array, it iterates every element and asserts each is a `typeof string`. This throws when the array contains non-string elements (numbers, booleans, nested objects, null). The reflector contract requires every element of `drop` and `merge[i].supersedes` to be a plain string.

Source

Thrown at packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts:292

	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`);
	const marker = readMarker(value.marker, index);
	if (typeof value.text !== 'string') {
		throw new Error(`Reflector merge[${index}].text must be a string`);
	}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Log the raw LLM output and identify which array element is non-string (the error names the field, e.g. `merge[0].supersedes`).
  2. Fix the reflector prompt to clarify that all IDs are strings and `null` must not appear inside arrays — omit the entry instead.
  3. If observation IDs in your system are numeric, ensure the reflector input renders them as quoted strings so the model echoes them back as strings.
  4. Consider enabling strict JSON-schema constrained decoding (structured outputs) on providers that support it so the array element type is enforced at the API level.

Example fix

// before: LLM returns { "drop": [42, "obs-2"] }
// Ensure the rendered observation log the reflector sees uses string IDs:
//   [obs-42] some observation text
// so the model echoes "obs-42" not 42.
// after: { "drop": ["obs-42", "obs-2"] }
Defensive patterns

Strategy: validation

Validate before calling

function isStringArray(value: unknown): value is string[] {
  return Array.isArray(value) && value.every((item) => typeof item === 'string');
}

// Pre-check before parseObservationLogReflectionJson:
const raw = JSON.parse(extractJsonObject(output));
for (const field of ['drop'] as const) {
  if (raw[field] !== undefined && !isStringArray(raw[field])) {
    throw new Error(`Pre-validation: ${field} contains non-string elements`);
  }
}

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) {
  logger.warn('Reflector array element type error', { field: 'see message', output });
  // skip reflection cycle
}

Prevention

When it happens

Trigger: The reflector LLM returns `{ "drop": [123, "obs-2"] }` (mixed number and string) or `{ "merge": [{ "supersedes": [null, "id1"] }] }` (null mixed in).

Common situations: The model confused numeric observation IDs with string IDs. The model emitted `null` placeholders for entries it could not supersede. A schema-drift where the reflector prompt changed the expected ID format.

Related errors


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