n8n-io/n8n · error · Error
Tool name "${RECALL_MEMORY_TOOL_NAME}" is reserved while epi
Error message
Tool name "${RECALL_MEMORY_TOOL_NAME}" is reserved while episodic memory is enabled. What it means
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.
Source
Thrown at packages/@n8n/agents/src/runtime/loop/runtime-context.ts:198
private createRecallMemoryToolForRun(
persistence: AgentPersistenceOptions | undefined,
existingTools: BuiltTool[],
executionCounter?: AgentExecutionCounter,
): BuiltTool | undefined {
const { memory, episodicMemory } = this.config;
if (
!memory ||
!episodicMemory ||
!isEpisodicMemoryEnabled(episodicMemory) ||
!hasEpisodicMemoryStore(memory)
) {
return undefined;
}
const scope = getEpisodicMemoryScope(persistence);
if (!scope) return undefined;
if (existingTools.some((tool) => tool.name === RECALL_MEMORY_TOOL_NAME)) {
throw new Error(
`Tool name "${RECALL_MEMORY_TOOL_NAME}" is reserved while episodic memory is enabled.`,
);
}
return createRecallMemoryTool({
memory,
config: episodicMemory,
scope,
executionCounter,
agentName: this.config.name,
});
}
/**
* Merge tool-attached `systemInstruction` fragments into the agent's
* configured instructions, split by stability:
*
* - `instructions`: fragments from tools present for the life of the run
* (base tools, deferred-tool controllers, the recall tool) plus theView on GitHub (pinned to 5ac6606e81)
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.
Example fix
// before — entry is a string
assignments: { assignments: ['status', { id: '2', name: 'ok', value: 1, type: 'number' }] }
// after
assignments: {
assignments: [
{ id: '1', name: 'status', value: 'ok', type: 'string' },
{ id: '2', name: 'ok', value: 1, type: 'number' },
],
} Defensive patterns
Strategy: type-guard
Validate before calling
function isRecord(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
}
const valid = assignmentItems.every(isRecord);
if (!valid) throw new Error('Every assignment must be an object'); Type guard
function isAssignment(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Passing a string id instead of an object; a partially-built assignment list with a stray primitive; an AI builder emitting shorthand entries.
Related errors
- Agent run was aborted
- ${turn.errorReason.message}
- MCP server "${this.config.name}": connectionTimeoutMs must b
- MCP server "${config.name}": provide either "url" or "comman
- Episodic memory requires a resolved embedding model before r
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/8bcdce80d837f6a5.
Report an issue: GitHub.