{"record":{"id":"8bcdce80d837f6a5","repo":"n8n-io/n8n","slug":"tool-name-recall-memory-tool-name-is-reserved","errorCode":null,"errorMessage":"Tool name \"${RECALL_MEMORY_TOOL_NAME}\" is reserved while episodic memory is enabled.","messagePattern":"Tool name \"(.+?)\" is reserved while episodic memory is enabled\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/runtime/loop/runtime-context.ts","lineNumber":198,"sourceCode":"\n\tprivate createRecallMemoryToolForRun(\n\t\tpersistence: AgentPersistenceOptions | undefined,\n\t\texistingTools: BuiltTool[],\n\t\texecutionCounter?: AgentExecutionCounter,\n\t): BuiltTool | undefined {\n\t\tconst { memory, episodicMemory } = this.config;\n\t\tif (\n\t\t\t!memory ||\n\t\t\t!episodicMemory ||\n\t\t\t!isEpisodicMemoryEnabled(episodicMemory) ||\n\t\t\t!hasEpisodicMemoryStore(memory)\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst scope = getEpisodicMemoryScope(persistence);\n\t\tif (!scope) return undefined;\n\t\tif (existingTools.some((tool) => tool.name === RECALL_MEMORY_TOOL_NAME)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Tool name \"${RECALL_MEMORY_TOOL_NAME}\" is reserved while episodic memory is enabled.`,\n\t\t\t);\n\t\t}\n\t\treturn createRecallMemoryTool({\n\t\t\tmemory,\n\t\t\tconfig: episodicMemory,\n\t\t\tscope,\n\t\t\texecutionCounter,\n\t\t\tagentName: this.config.name,\n\t\t});\n\t}\n\n\t/**\n\t * Merge tool-attached `systemInstruction` fragments into the agent's\n\t * configured instructions, split by stability:\n\t *\n\t * - `instructions`: fragments from tools present for the life of the run\n\t *   (base tools, deferred-tool controllers, the recall tool) plus the","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/loop/runtime-context.ts#L180-L216","documentation":"Thrown by the Set node validator (v3.3+) for each assignment entry that is not an object. Every assignment must be a record with id, name, value, and type; a primitive or array entry is rejected and the loop continues to the next entry.","triggerScenarios":"During iteration of parameters.assignments.assignments[], an entry fails isRecord (it is null, an array, or a primitive). The issue is reported at parameterPath parameters.assignments.assignments[index] and the loop continues.","commonSituations":"Passing a string id instead of an object; a partially-built assignment list with a stray primitive; an AI builder emitting shorthand entries.","solutions":["Replace the offending entry with a full object: { id, name, value, type }.","Generate a stable id for each entry (e.g. a uuid or incrementing string).","Remove the entry if it was added by mistake."],"exampleFix":"// before — entry is a string\nassignments: { assignments: ['status', { id: '2', name: 'ok', value: 1, type: 'number' }] }\n\n// after\nassignments: {\n  assignments: [\n    { id: '1', name: 'status', value: 'ok', type: 'string' },\n    { id: '2', name: 'ok', value: 1, type: 'number' },\n  ],\n}","handlingStrategy":"type-guard","validationCode":"function isRecord(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nconst valid = assignmentItems.every(isRecord);\nif (!valid) throw new Error('Every assignment must be an object');","typeGuard":"function isAssignment(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Build assignments from a typed factory so entries are always objects.","Reject primitive entries at generation time.","Unit-test the assignment builder against malformed inputs."],"tags":["set-node","assignments","parameters","node-version"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}