FishMem

A safe migration from mem0: contracts before cutover

A safe migration from mem0: contracts before cutover
July 25, 2026Guides12 min read

Map familiar operations, document semantic differences, replay representative traces, and preserve rollback until stored state and recall pass.

A migration between memory systems is not an import rename. Two APIs can both expose add, search, update, and delete while disagreeing about extraction, identity, temporal fields, filters, pagination, history, and when a write is considered complete.

The safe path begins with the deployed contract—not a current quickstart—and ends only when persisted state and downstream answers pass an observation window.

The short version

  • Inventory the exact mem0 client, service, configuration, and operations your product uses.
  • Export source records and preserve them independently of either runtime.
  • Map ownership, inference, timestamps, metadata, history, and deletion explicitly.
  • Replay redacted production-shaped traces before moving live traffic.
  • Cut over by cohort with independent rollback and dual-read evidence.
  • Do not claim compatibility beyond the operations and behaviors you verified.

Freeze the source contract

Record the mem0 SDK and service versions, hosting mode, vector backend, model configuration, custom prompts, graph or reranking features, default limits, and every field your application reads from responses. Capture representative requests and outputs before changing dependencies.

At minimum, inventory:

  • add behavior with and without inference;
  • search queries, filters, ranking, and top-k;
  • list and pagination semantics;
  • get, update, history, and delete;
  • user, agent, run, session, app, or organization scope;
  • metadata and timestamp usage;
  • batch operations and rate limits;
  • export format and identifiers;
  • error handling and retry behavior.

The contract in production may be older or more customized than the documentation you read today. Treat current documentation as a reference, not evidence of what your application actually deployed.

Map semantics before fields

ConcernSource questionFishMem decision
Outer tenancyWhich account or project owns the data?Bind the authenticated project namespace; never trust it from the body
User and agent scopeWho should retrieve the record?Map to user_id, agent_id, and run_id only when meanings match
InferenceIs the export already a durable fact or raw conversation?Use infer=false for distilled records; evaluate inference separately
TimeIs a timestamp ingestion time, event time, or validity?Preserve known meanings; never invent precision
HistoryDoes update replace, append, or supersede?Choose current-state and immutable-history behavior explicitly
CompletionWhen does the source call report success?Deterministic writes complete synchronously; inferred Cloud writes return a durable event

Do not re-infer already distilled memories

A mem0 export generally contains memory records, not the original conversation that produced them. Passing those records through another extraction model changes wording, drops detail, and makes it impossible to distinguish migration loss from product behavior.

Import the record content deterministically with infer=false, carry source identifiers in migration provenance, and preserve the original export outside FishMem until the observation window ends. If you also have raw conversations and want to compare a new inference policy, treat that as a separate experiment.

Build a migration ledger

Each source record needs a durable migration status. A practical ledger contains:

  • source system and version;
  • source record ID and stable source export ID;
  • source scope and mapped FishMem namespace/scope;
  • content hash and metadata hash;
  • event, creation, and update timestamps with their known meanings;
  • FishMem idempotency key and destination record ID;
  • status, attempts, error, and verification result;
  • cohort and rollback owner.

Generate idempotency keys from a stable migration identity rather than a loop counter. A restarted importer should converge on the same destination records.

Replay production-shaped traces

Create an isolated FishMem project and replay redacted user journeys. Compare more than search text:

  1. persisted records and scope;
  2. metadata and timestamps;
  3. history after update or supersession;
  4. search candidates, ranking, and evidence;
  5. assembled context and downstream answer;
  6. latency, token use, and failure rate;
  7. retry, deletion, and export behavior.

Include cases with no relevant memory, conflicting facts, changed preferences, duplicate requests, missing metadata, and users with similar histories. A migration that only tests happy-path adds does not test retrieval or isolation.

Use shadow reads before changing answers

During a shadow phase, keep the source system authoritative. Send the same query to FishMem, record both result sets, and compare the downstream decision without exposing FishMem output to the user. This reveals ranking and formatting differences before they affect production.

Do not require byte-identical context. Different systems may return different phrasing and order while supporting the same correct decision. Define required evidence, forbidden evidence, and expected answer behavior.

Cut over by cohort

  1. Offline import: migrate a frozen export and reconcile counts and hashes.
  2. Shadow read: compare retrieval and answers while mem0 remains authoritative.
  3. Dual write: write new commands to both systems with separate observability; do not let one hide the other's failure.
  4. Read canary: enable FishMem for an internal or small user cohort.
  5. Expand: move one workspace or percentage band at a time under explicit error and quality gates.
  6. Retire: only after the observation window, final export, deletion plan, and rollback decision.

Keep rollback independent

A rollback that depends on the destination system being healthy is not independent. Retain the source export, source-to-destination ledger, previous read path, and a defined way to reconcile writes made during the canary period.

Before retirement, export FishMem and test that the namespace can be restored into an empty target. This proves you can leave the destination as well as enter it.

Common migration failures

Matching field names are treated as matching ownership

A user_id may mean an end user, an account, or a conversation participant. Map the authorization meaning, not just the string.

Creation time is promoted to event time

Import timestamps may describe when a row was stored, not when the underlying fact became true. Keep them separate or leave event time unknown.

Every source memory is re-extracted

The migration changes content while claiming to preserve it. Import distilled records verbatim, then evaluate any new extraction policy on raw evidence.

Dual write hides partial failure

If the application reports success when either backend succeeds, the two stores drift. Record both outcomes and define which one is authoritative at each phase.

Acceptance criteria

  • Every in-scope source record is imported once or has a reviewed error.
  • Structural scope and authorization tests pass across adversarial users and projects.
  • Required current and historical cases pass the held-out set.
  • Retries do not create duplicates; changed commands conflict visibly.
  • Delete, export, and restore journeys are verified.
  • Production canary quality, error rate, and latency stay within agreed bounds.
  • The rollback owner can execute the documented path without new code.

Compatibility is evidence, not a slogan

FishMem is not affiliated with mem0 and does not claim universal drop-in compatibility. The migration surface is intentionally familiar, but semantic differences remain. Publish the versions and cases you verified, and describe everything else as untested.

Further reading

Read next