{"record":{"id":"8aaf9f04db6e84a6","repo":"mem0ai/mem0","slug":"resource-not-found-path-8aaf9f","errorCode":null,"errorMessage":"Resource not found: ${path}","messagePattern":"Resource not found: (.+?)","errorType":"http","errorClass":"NotFoundError","httpStatus":404,"severity":"error","filePath":"integrations/openclaw/backend/platform.ts","lineNumber":62,"sourceCode":"      url += `?${qs}`;\n    }\n\n    const fetchOpts: RequestInit = {\n      method,\n      headers: this.headers,\n      signal: AbortSignal.timeout(30_000),\n    };\n    if (opts?.json) {\n      fetchOpts.body = JSON.stringify(opts.json);\n    }\n\n    const resp = await fetch(url, fetchOpts);\n\n    if (resp.status === 401) {\n      throw new AuthError();\n    }\n    if (resp.status === 404) {\n      throw new NotFoundError(path);\n    }\n    if (resp.status === 400) {\n      let detail: string;\n      try {\n        const body = (await resp.json()) as Record<string, unknown>;\n        detail =\n          ((body.detail ?? body.message ?? JSON.stringify(body)) as string) ??\n          resp.statusText;\n      } catch {\n        detail = resp.statusText;\n      }\n      throw new APIError(path, detail);\n    }\n    if (!resp.ok) {\n      let detail: string = resp.statusText;\n      try {\n        const body = (await resp.json()) as Record<string, unknown>;\n        detail = (body.detail ?? body.message ?? resp.statusText) as string;","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/integrations/openclaw/backend/platform.ts#L44-L80","documentation":"POST /auth/login looked up the submitted email and found no user row, so it returns the generic 401 'Invalid email or password'. A dummy bcrypt verify (dummy_verify_password) is executed first to keep response timing comparable to the wrong-password case, preventing user-enumeration via timing. The email not existing and the password being wrong are deliberately indistinguishable.","triggerScenarios":"Logging in before registering (users table empty or email absent); typo/case mismatch in the email (EmailStr normalizes the domain but the lookup is exact); logging in against the wrong environment (dev vs prod) or after a DB reset removed users.","commonSituations":"Fresh deployment where /setup was never completed; environment mismatch in scripts (BASE_URL pointing at another instance); email entered with different capitalization in the local part; database volume recreated so all accounts vanished.","solutions":["Confirm an account exists: on a fresh instance complete /setup (POST /auth/register) first; otherwise verify the email with the admin's user list.","Check you are hitting the correct server BASE_URL/environment.","Use the exact email used at registration; if the DB was reset, re-register and re-issue credentials.","If access is truly lost and no admin remains, an operator with DB access can remove users to reopen /setup."],"exampleFix":"# before\nlogin(\"admim@example.com\", pw)  # typo -> 401\n\n# after\nlogin(\"admin@example.com\", pw)","handlingStrategy":"validation","validationCode":"import re\n\nEMAIL_RE = re.compile(r\"^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\")\n\ndef validate_login(email: str, password: str) -> None:\n    if not EMAIL_RE.match(email or \"\"):\n        raise ValueError(f\"Not a valid email: {email!r}\")\n    if not password:\n        raise ValueError(\"Password is empty\")","typeGuard":"def looks_like_email(value: str | None) -> bool:\n    return isinstance(value, str) and \"@\" in value and \".\" in value.split(\"@\")[-1]","tryCatchPattern":"if resp.status_code == 401 and resp.json().get(\"detail\") == \"Invalid email or password.\":\n    # do NOT distinguish wrong-email vs wrong-password; re-verify against the intended environment\n    raise CredentialsError(\"Check email/password and that BASE_URL targets the right instance\")","preventionTips":["Verify /auth/setup-status shows setup complete before adding login to flows.","Pin BASE_URL per environment so credentials are never sent to the wrong one.","Store the registered email exactly (watch local-part case)."],"tags":["auth","login","http-401","rest-server"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}