{"record":{"id":"0091108484cbdfe0","repo":"n8n-io/n8n","slug":"reflector-output-did-not-contain-a-json-object","errorCode":null,"errorMessage":"Reflector output did not contain a JSON object","messagePattern":"Reflector output did not contain a JSON object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts","lineNumber":282,"sourceCode":"\tif (hasAncestorIn(id, ownActionIds, activeById)) return false;\n\tif (hasAncestorIn(id, allSameKindActionIds, activeById)) return true;\n\treturn !hasAncestorIn(id, otherRemovalIds, activeById);\n}\n\nfunction normalizeReplacementParentId(\n\tparentId: string | null | undefined,\n\tactiveById: Map<string, ObservationLogEntry>,\n\tremovedIds: Set<string>,\n): string | null | undefined {\n\tif (parentId === undefined || parentId === null) return parentId;\n\treturn activeById.has(parentId) && !removedIds.has(parentId) ? parentId : null;\n}\n\nfunction extractJsonObject(output: string): string {\n\tconst start = output.indexOf('{');\n\tconst end = output.lastIndexOf('}');\n\tif (start === -1 || end === -1 || end < start) {\n\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');","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/memory/observation-log-reflector.ts#L264-L300","documentation":"Warning thrown by the unknown-config-keys validator when a node's config object has top-level keys that are not recognised NodeConfig fields. Unrecognised keys are silently dropped during serialization, which leaves the node with empty parameters — a common LLM/MCP-client mistake of placing parameters directly under config instead of config.parameters.","triggerScenarios":"Object.keys(node.config) includes any key not in KNOWN_CONFIG_KEYS (parameters, credentials, name, position, webhookId, disabled, notes, notesInFlow, executeOnce, retryOnFail, maxTries, waitBetweenTries, alwaysOutputData, onError, extendsCredential, pinData, output, subnodes) and not starting with '_'.","commonSituations":"An LLM puts node inputs at the top level (e.g. config.url instead of config.parameters.url); a migration that nested the schema; copying an example that predated the NodeConfig contract.","solutions":["Move the unknown keys inside config.parameters so they survive serialization.","If a key is a legitimate new NodeConfig field, add it to the KNOWN_CONFIG_KEYS map (and to the NodeConfig type).","Prefix truly internal markers with '_' so they are intentionally ignored."],"exampleFix":"// before — url sits at the top level of config and gets dropped\nhttpRequest({ name: 'Get', url: 'https://example.com' });\n\n// after\nhttpRequest({ name: 'Get', parameters: { url: 'https://example.com' } });","handlingStrategy":"validation","validationCode":"const KNOWN_CONFIG_KEYS = new Set([\n  'parameters', 'credentials', 'name', 'position', 'webhookId', 'disabled', 'notes',\n  'notesInFlow', 'executeOnce', 'retryOnFail', 'maxTries', 'waitBetweenTries',\n  'alwaysOutputData', 'onError', 'extendsCredential', 'pinData', 'output', 'subnodes',\n]);\n\nfunction findUnknownConfigKeys(config: Record<string, unknown>): string[] {\n  return Object.keys(config).filter((k) => !KNOWN_CONFIG_KEYS.has(k) && !k.startsWith('_'));\n}\n\nconst unknown = findUnknownConfigKeys(node.config);\nif (unknown.length) throw new Error(`Move into config.parameters: ${unknown.join(', ')}`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always nest node inputs under config.parameters, never at the top level of config.","For internal markers, prefix keys with '_' so they are intentionally ignored.","When adding a real NodeConfig field, update KNOWN_CONFIG_KEYS and the NodeConfig type together."],"tags":["serialization","config","parameters","ai-generated"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}