mem0ai/mem0 · warning · NodeOperationError

Timed out waiting for memory event ${eventId} to complete

Error message

Timed out waiting for memory event ${eventId} to complete

What it means

POST /auth/register is only for bootstrapping: it creates the first admin account and is blocked once any user row exists (checked via a COUNT before insert). Once count > 0, further registrations get 403 'Registration is closed' regardless of role or intent — subsequent users must be created by an admin through the user-management endpoints.

Source

Thrown at integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts:621

): Promise<IDataObject | IDataObject[]> {
	for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
		const event = await request('GET', `/v1/event/${encodeURIComponent(eventId)}/`);
		const status = event.status as string;
		if (status === 'SUCCEEDED') {
			// Match the shape of search/getAll (a clean array); fall back to the envelope.
			return Array.isArray(event.results) ? (event.results as IDataObject[]) : event;
		}
		if (status === 'FAILED') {
			const reason = (event.error as string) || (event.message as string) || 'unknown error';
			throw new NodeOperationError(
				ctx.getNode(),
				`Mem0 memory event ${eventId} failed: ${reason}`,
				{ itemIndex },
			);
		}
		await sleep(POLL_INTERVAL_MS);
	}
	throw new NodeOperationError(
		ctx.getNode(),
		`Timed out waiting for memory event ${eventId} to complete`,
		{ itemIndex },
	);
}

View on GitHub (pinned to 001c235229)

Solutions

  1. Log in with the existing admin (POST /auth/login) instead of registering again.
  2. To add users, use the admin user-management API while authenticated as admin.
  3. If the instance should truly start over, wipe the users table (or the whole DB volume) and re-run /setup.
  4. Make deployment scripts idempotent: check GET /auth/setup-status first and skip register when needsSetup is false.

Example fix

# before
requests.post(f"{BASE}/auth/register", json={...})  # 403 on initialized server

# after
status = requests.get(f"{BASE}/auth/setup-status").json()
if status["needsSetup"]:
    requests.post(f"{BASE}/auth/register", json={...})
else:
    token = login(existing_admin_email, password)
Defensive patterns

Strategy: validation

Validate before calling

def bootstrap_admin(base: str, name: str, email: str, password: str) -> dict:
    status = requests.get(f"{base}/auth/setup-status").json()
    if not status["needsSetup"]:
        return requests.post(f"{base}/auth/login", json={"email": email, "password": password}).json()
    return requests.post(f"{base}/auth/register",
                         json={"name": name, "email": email, "password": password}).json()

Type guard

def needs_setup(setup_status: dict) -> bool:
    return bool(setup_status.get("needsSetup"))

Try / catch

if resp.status_code == 403 and "Registration is closed" in resp.text:
    creds = login(base, admin_email, admin_password)  # instance already initialized
else:
    resp.raise_for_status()

Prevention

When it happens

Trigger: Calling POST /auth/register a second time after /setup was completed; two team members racing to claim the first-admin slot; attempting to self-register on an already-initialized deployment.

Common situations: Re-running a bootstrap script against an already-initialized environment; CI recreating containers but keeping the DB volume (users persist, so register 403s); confusion between first-admin bootstrap and normal user creation.

Understand the failure class

Related errors


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/7c5c11df25d9401f. Report an issue: GitHub.