{"record":{"id":"5d518b931473746c","repo":"mem0ai/mem0","slug":"add-requires-at-least-one-of-user-id-agent-id-ru","errorCode":null,"errorMessage":"Add requires at least one of User ID, Agent ID, Run ID, or App ID","messagePattern":"Add requires at least one of User ID, Agent ID, Run ID, or App ID","errorType":"exception","errorClass":"NodeOperationError","httpStatus":null,"severity":"warning","filePath":"integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts","lineNumber":456,"sourceCode":"\t\t\t\t\t\t\tbody.custom_categories =\n\t\t\t\t\t\t\t\ttypeof addFields.custom_categories === 'string'\n\t\t\t\t\t\t\t\t\t? JSON.parse(addFields.custom_categories as string)\n\t\t\t\t\t\t\t\t\t: addFields.custom_categories;\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\tthrow new NodeOperationError(\n\t\t\t\t\t\t\t\tthis.getNode(),\n\t\t\t\t\t\t\t\t'Invalid JSON in \"Custom Categories\" field',\n\t\t\t\t\t\t\t\t{ itemIndex: i },\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (addFields.includes) body.includes = addFields.includes;\n\t\t\t\t\tif (addFields.excludes) body.excludes = addFields.excludes;\n\n\t\t\t\t\t// API requires at least one entity id — fail clearly instead of a raw 4xx.\n\t\t\t\t\tif (!body.user_id && !body.agent_id && !body.run_id && !body.app_id) {\n\t\t\t\t\t\tthrow new NodeOperationError(\n\t\t\t\t\t\t\tthis.getNode(),\n\t\t\t\t\t\t\t'Add requires at least one of User ID, Agent ID, Run ID, or App ID',\n\t\t\t\t\t\t\t{ itemIndex: i },\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst addResp = await request('POST', '/v3/memories/add/', body);\n\t\t\t\t\tconst waitForCompletion = this.getNodeParameter('waitForCompletion', i, true) as boolean;\n\t\t\t\t\tconst addStatus = addResp.status as string | undefined;\n\t\t\t\t\tconst isTerminal = addStatus === 'SUCCEEDED' || addStatus === 'FAILED';\n\n\t\t\t\t\t// Add returns {event_id, status:PENDING|RUNNING}; poll until terminal when asked to wait.\n\t\t\t\t\tif (waitForCompletion && addResp.event_id && !isTerminal) {\n\t\t\t\t\t\tresponseData = await pollEvent(request, addResp.event_id as string, this, i);\n\t\t\t\t\t} else if (addStatus === 'FAILED') {\n\t\t\t\t\t\tthrow new NodeOperationError(\n\t\t\t\t\t\t\tthis.getNode(),\n\t\t\t\t\t\t\t`Mem0 memory add failed: ${(addResp.message as string) || 'unknown error'}`,","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts#L438-L474","documentation":"POST /memories (add_memory) requires the new memory to be attributable to at least one scope: user_id, agent_id, or run_id. The MemoryCreate body may legitimately omit several fields, but if all three identifiers are empty/None the server rejects with 400 before calling memory.add, because unscoped memories cannot be retrieved or filtered later.","triggerScenarios":"POST /memories with a body containing only messages and metadata (no user_id/agent_id/run_id); passing empty strings for all identifiers; a client that builds the payload dynamically and the identifier variables happened to be undefined/None.","commonSituations":"Porting code from the SDK where a default user_id was set elsewhere; frontend form where the user/agent field is optional and was left blank; batch scripts templating payloads with variables that resolve to empty strings.","solutions":["Include at least one of user_id, agent_id, or run_id in the JSON body.","If your app has a single tenant, adopt a constant default (e.g. user_id=\"default\") for all writes.","Validate the payload client-side before sending so the failure is caught locally."],"exampleFix":"# before\ncurl -X POST http://localhost:3000/memories -H 'Authorization: Bearer t' \\\n  -H 'Content-Type: application/json' -d '{\"messages\": [{\"role\": \"user\", \"content\": \"prefers tea\"}]}'\n\n# after\ncurl -X POST http://localhost:3000/memories -H 'Authorization: Bearer t' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"messages\": [{\"role\": \"user\", \"content\": \"prefers tea\"}], \"user_id\": \"alice\"}'","handlingStrategy":"validation","validationCode":"def validate_memory_payload(payload: dict) -> dict:\n    if not any(payload.get(k) for k in (\"user_id\", \"agent_id\", \"run_id\")):\n        payload[\"user_id\"] = payload.get(\"user_id\") or \"default\"\n    assert any(payload.get(k) for k in (\"user_id\", \"agent_id\", \"run_id\"))\n    return payload","typeGuard":"def has_scope(payload: dict) -> bool:\n    return any(bool(payload.get(k)) for k in (\"user_id\", \"agent_id\", \"run_id\"))","tryCatchPattern":null,"preventionTips":["Make identifier fields required in client-side schemas/DTOs.","Reject empty-string identifiers at the client boundary.","Integration-test the create path with a minimal payload to catch regressions."],"tags":["validation","http-400","memories","rest-server"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}