7.3 KiB
Gotchas — the things that aren't a clean 1:1
Swapping Memory for MemoryClient is mostly mechanical. These items are not mechanical: they
change behavior, move responsibility off the developer's machine, or have no direct equivalent.
Every one that applies to the project belongs in the plan's "Concerns & decisions needed"
section, phrased as a decision for the developer — never silently resolved.
1. Data does not migrate with the code
Migrating the code does not move the memories. Anything stored in the local vector store / history DB stays there; the hosted account starts empty. This is the most surprising gap, so call it out prominently. Data migration is out of scope unless the developer explicitly asks. If they do, treat it as a separate, opt-in task:
- Hosted Qdrant + Python OSS SDK: the repo ships
scripts/oss-to-platform-migrate.sh(curl -fsSL https://raw.githubusercontent.com/mem0ai/mem0/main/scripts/oss-to-platform-migrate.sh | bash, documented at https://docs.mem0.ai/migration/oss-to-platform). It needspython3, signs in to the Platform (--email/--codeor--api-key), exports a scope (--user-id,--agent-id,--run-idor--all) from the Qdrant collection (--qdrant-url,--qdrant-api-key,--qdrant-collection, defaultmem0) to a JSON file under~/.mem0/migrations/, and imports it withinfer: False(stored verbatim, no re-extraction).--export-only/--import-onlysplit the two steps so the export can be reviewed first. Don't run it unprompted: it logs in and writes under~/.mem0/. - Anything else: other vector stores are not supported by the script (the docs say hosted Qdrant
only), and its export is written for the Python OSS SDK, so TS-written data is not documented as
supported. The fallback is to read everything from the OSS store (
get_allper entity) and re-addit withinfer=False.
2. Self-hosting / data residency
A local or self-hosted vector store sometimes exists on purpose — compliance, data residency, air- gapped deployment, cost. Moving to the managed platform sends memory content to mem0's servers. Don't assume that's acceptable; flag it as an explicit decision, especially for regulated domains.
3. Local models move server-side
If the OSS config used specific local/self-chosen models (e.g. Ollama, a particular embedder, a non-OpenAI LLM for fact extraction), those choices disappear — extraction and embedding now run on the platform with the platform's configuration. Memory content and quality may shift as a result. Flag where the project depended on a specific model.
4. Graph memory
The external graph store (graph_store, enable_graph; Neo4j/Memgraph/Kuzu/AGE) was removed from OSS
(Python 2.0.0, TS 3.0.0). On the platform graph memory is built in and always on: nothing to enable or
configure, and it only influences ranking (no separate relations payload, no typed relationships,
no direct graph queries). Flag any project code that queried its own graph store or read relations.
See https://docs.mem0.ai/platform/features/graph-memory.
5. Custom prompts / extraction config
custom_fact_extraction_prompt (TS customPrompt) was renamed custom_instructions
(customInstructions), and custom_update_memory_prompt is deprecated (fold it into
custom_instructions). On the platform custom_instructions is a project-level setting:
client.project.update(custom_instructions=...) (TS client.updateProject({ customInstructions })),
or per call via custom_instructions= on add().
Flag any custom prompt the project relied on so the developer can decide where to re-apply it.
6. Every call is now a network request
Local calls become remote API calls. That introduces latency, network failures, timeouts, rate
limits, and per-call cost. Flag mem0 calls on hot paths or in tight loops, and recommend adding
error handling / retries / timeouts where the old local calls were effectively infallible. For async
apps, use AsyncMemoryClient (Python) so calls don't block the event loop.
7. API key & secrets
The hosted client needs MEM0_API_KEY. It must come from the environment / a secrets manager, never
hardcoded. Ensure it's added to .env.example, local .env, CI, and deployment config. Without it
the client fails to initialize. The Python client reads MEM0_API_KEY itself when api_key is
omitted; the TS client does not, so pass apiKey: process.env.MEM0_API_KEY! explicitly.
8. Dropped constructor args & legacy options
org_id / project_id (Python) and organizationId / projectId (TS) are no longer passed to the
constructor, they're resolved from the API key. Per-call legacy options like async_mode,
output_format, and enable_graph are gone. Remove them rather than leaving dead args.
9. Return-shape and default drift
add()is asynchronous on the platform: it returns{"status": "PENDING", "event_id": "..."}(eventIdon the TS client, which camelCases response keys) instead of the created memories, so new memories may not be searchable yet when it returns (infer=Falseis synchronous and returnsresults). Code that readid/memoryfromadd()results, or searched immediately after adding (tests, read-your-writes flows), needs a decision. Neither SDK exposes an event-poll method;GET /v1/event/{event_id}/is REST only.searchreturns{"results": [...]}on both sides;get_allis paginated on the platform (count/next/previous/results). Code that limited viatop_konget_allshould move topage/page_size(defaultpage_size100).- Defaults differ: OSS
searchusestop_k=20, the platform usestop_k=10. Both default torerank=false. OSSthresholddefaults to 0.1; on the platform it is a server-side cutoff, not a floor on the returnedscore. Result counts can change even when the call looks equivalent; passtop_kexplicitly.
10. reset() is much more destructive
The OSS reset() wipes the local store. Python MemoryClient.reset() exists but calls delete_users(),
which deletes all users, agents, sessions and memories on the platform. The TS client has no reset()
(deleteUsers() with no arguments does the same). Flag any reset() call (often test teardown) and
suggest delete_all scoped to the test entity instead. delete_all(filters=...) is not a valid
platform form: pass user_id/agent_id/app_id/run_id directly.
The hosted wipe is also incomplete: Python reset() and TS deleteUsers() with no arguments call
users() once and delete only the entities on that first page, so a project with more entities than
one page keeps the rest. Re-run until it raises No entities to delete, or delete per entity
(delete_users(user_id=...) / deleteUsers({ userId }), paging TS with users({ page, pageSize })).
11. Filters and update/add signatures
- Platform
filtersonly accept an allowlist of top-level keys; custom metadata must be nested under"metadata"and supports onlyeq/ne/contains(whole value or list member, not a substring; nonin; use aNOTlist, e.g.{"NOT": [{"categories": {"in": [...]}}]}). OSS filters on arbitrary metadata keys or richer operators need rewriting or a decision. - Python
updatetakestext=as a keyword (a positional string breaks); TSupdaterequires an options object and TSaddrequiresMessage[], not a bare string.