{"record":{"id":"2f3aa719cf9b1f88","repo":"mem0ai/mem0","slug":"llm-extraction-failed-e-2f3aa7","errorCode":null,"errorMessage":"LLM extraction failed: {e}","messagePattern":"LLM extraction failed: (.+?)","errorType":"exception","errorClass":"LLMError","httpStatus":null,"severity":"error","filePath":"mem0/memory/main.py","lineNumber":969,"sourceCode":"            last_k_messages=last_messages,\n            custom_instructions=custom_instr,\n        )\n\n        try:\n            response = self.llm.generate_response(\n                messages=[\n                    {\"role\": \"system\", \"content\": system_prompt},\n                    {\"role\": \"user\", \"content\": user_prompt},\n                ],\n                response_format={\"type\": \"json_object\"},\n            )\n        except Exception as e:\n            # Re-raise so callers can implement provider fallback / retry.\n            # The original silent ``return []`` made upstream callers unable to\n            # distinguish \"LLM unavailable\" (429/5xx/timeout) from \"LLM\n            # extracted no facts\" -- both surfaced as an empty list.\n            logger.error(f\"LLM extraction failed: {e}\")\n            raise LLMError(f\"LLM extraction failed: {e}\") from e\n\n        # Parse response\n        try:\n            response = remove_code_blocks(response)\n            if not response or not response.strip():\n                extracted_memories = []\n            else:\n                try:\n                    extracted_memories = json.loads(response, strict=False).get(\"memory\", [])\n                except json.JSONDecodeError:\n                    extracted_json = extract_json(response)\n                    extracted_memories = json.loads(extracted_json, strict=False).get(\"memory\", [])\n        except Exception as e:\n            logger.error(f\"Error parsing extraction response: {e}\")\n            extracted_memories = []\n\n        if not extracted_memories:\n            # Save messages even if nothing extracted","sourceCodeStart":951,"sourceCodeEnd":987,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0/memory/main.py#L951-L987","documentation":"Raised as LLMError by the memory-extraction helper when the underlying LLM call inside Memory.add() fails for any reason — rate limits (429), auth errors, timeouts, malformed provider responses, or network failures. The SDK deliberately re-raises (instead of returning an empty extraction) so callers can distinguish 'LLM unavailable' from 'no facts found' and implement retry or provider fallback; the original exception is chained via 'from e' and logged first.","triggerScenarios":"m.add(...) with an expired or wrong OPENAI_API_KEY; hitting provider rate limits under load; Ollama/vLLM local server down or timing out; a custom LLM provider whose generate_response raises on unexpected response shapes; transient 5xx from the provider.","commonSituations":"Production traffic spikes exhausting API quotas; local model server (Ollama, LM Studio) not started; mistyped or rotated API keys; flaky networks between the service and the provider; switching LLM provider configs without updating credentials.","solutions":["Inspect the chained cause (exc.__cause__) — the fix differs for 429 (back off / raise limits) vs 401 (fix the key) vs timeout (increase timeout or use a faster model).","Retry with exponential backoff for transient errors (429/5xx/timeouts); LLMError here is a reliable marker that extraction failed, not that no memories were found.","Verify provider config in MemoryConfig (llm.provider and its api_key/model) and test connectivity independently.","Add a fallback provider or queue the add() for later if the LLM is degraded.","Catch mem0.memory.utils.LLMError (or LLMError from mem0.utils) specifically so validation errors are not swallowed."],"exampleFix":"# before\nm.add(\"user likes espresso\", user_id=\"u1\")  # raises LLMError on 429, app crashes\n\n# after\nfrom mem0.memory.utils import LLMError\nimport time\nfor attempt in range(3):\n    try:\n        m.add(\"user likes espresso\", user_id=\"u1\")\n        break\n    except LLMError as e:\n        if attempt == 2 or \"401\" in str(e.__cause__):\n            raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"# preflight: verify the configured LLM is reachable before relying on add()\n# e.g. for openai-style providers\ntry:\n    m.llm.generate_response([{ \"role\": \"user\", \"content\": \"ping\" }])\nexcept Exception as e:\n    raise RuntimeError(f\"LLM provider unreachable: {e}\")","typeGuard":"def is_retryable_llm_error(exc) -> bool:\n    text = str(getattr(exc, \"__cause__\", exc)).lower()\n    return any(s in text for s in (\"429\", \"rate limit\", \"timeout\", \"503\", \"502\", \"connection\"))","tryCatchPattern":"from mem0.memory.utils import LLMError\nimport time\n\ndef add_with_retry(m, msg, *, attempts=4, **kw):\n    last = None\n    for i in range(attempts):\n        try:\n            return m.add(msg, **kw)\n        except LLMError as e:\n            last = e\n            if not is_retryable_llm_error(e) or i == attempts - 1:\n                raise\n            time.sleep(2 ** i)\n    raise last","preventionTips":["Wrap add() in exponential-backoff retry for 429/5xx/timeout; re-raise immediately on auth errors.","Health-check the LLM provider at service startup (bad keys fail fast there, not mid-conversation).","Differentiate LLMError (extraction failed) from a normal 'no memories extracted' result — do not treat it as empty.","Watch rate-limit headers from your provider and throttle add() calls below quota."],"tags":["llm","retry","rate-limit","add","network"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}