May 06, 2026

mem0 Plugin Ghost Writes: memory_add Success with Empty Database

The openclaw-mem0 plugin reports successful memory writes but SQLite database remains empty due to duplicate plugin registration or uncommitted transactions.

πŸ” Symptoms

Primary Manifestation

The memory_add tool executes without throwing exceptions and returns a success message, yet no data persists to the SQLite database.


# User observes successful return from memory_add
$ openclaw run "Remember my favorite color is blue"
βœ“ Memory stored successfully (v1.2.3)

# But database remains empty
$ sqlite3 ~/.mem0/vector_store.db 'SELECT count(*) FROM vectors;'
0

Diagnostic CLI Commands

bash

Check database file existence and size

$ ls -la ~/.mem0/vector_store.db -rw-rw-r– 1 user user 0 Jan 15 10:30:00 ~/.mem0/vector_store.db

Verify database schema integrity

$ sqlite3 ~/.mem0/vector_store.db ‘SELECT name FROM sqlite_master WHERE type=“table”;’ (empty result - no tables exist)

Cross-check the active memory store

$ openclaw debug –show-active-stores [ERROR] No stores configured. Using in-memory fallback.

Secondary Indicators

  • Repeated `memory_add` calls produce identical `count(*)` results (always 0)
  • No exception or error message during write operations
  • Migration scripts report transfer completion without actual data movement
  • Plugin logs show "Stored in memory" without "Committed to disk" confirmation

🧠 Root Cause

Architectural Analysis

The ghost write behavior stems from a state synchronization failure between the plugin’s in-memory representation and the persistent storage layer. The issue manifests through two distinct failure modes:

Failure Mode 1: Duplicate Registration Desynchronization

When register() is invoked multiple times (a known issue related to #77822), the plugin creates multiple closure-scoped instances with divergent state:


// Simplified representation of the bug
let memoryInstance = null;  // Module-level singleton

function register(config) {
    // First call: memoryInstance = new Mem0Manager()
    // Second call: memoryInstance = new Mem0Manager() // NEW instance
    // Writes go to SECOND instance (orphaned on garbage collection)
    memoryInstance = new Mem0Manager(config);
}

function memory_add(content) {
    // This instance never gets committed to disk
    return memoryInstance.add(content);
}

The first register() call initializes an instance bound to ~/.mem0/, but a subsequent register() call creates a second instance. All subsequent writes target the second instance, which may operate on a different database path or hold an uncommitted transaction buffer.

Failure Mode 2: Transaction Isolation Without Commit

The mem0 SQLite backend may execute writes within an uncommitted transaction context:


// Transaction opened but never committed
await db.transaction(async (tx) => {
    await tx.insert('vectors', { content, embedding });
    // Missing: await tx.commit();
});
// Data exists in transaction log but not in main database file

Root Cause Vectors

VectorDescriptionImpact
Duplicate register() callsMultiple plugin registration events during initializationWrites target orphaned instance
Transaction scope leakWrite operations wrapped in uncommitted transactionsData invisible until rollback
Path resolution divergence~/.mem0/ vs /tmp/mem0/ vs absolute pathSilent writes to wrong location
Process lifecycle mismatchIn-memory buffer flushed to different DB connectionGhost data in disconnected context

Environment-Specific Triggers

  • Docker: Volume mount conflicts cause separate processes to write to different database files
  • macOS: `~/Library/Application Support/mem0/` vs `~/.mem0/` path discrepancy
  • Hot reload: Development servers re-register plugins without resetting state

πŸ› οΈ Step-by-Step Fix

Phase 1: Isolate the Plugin Instance

Before proceeding, ensure only one instance of the mem0 plugin is active:

bash

Kill all existing openclaw processes

$ pkill -f “openclaw” || true

Verify clean slate

$ ps aux | grep openclaw (empty output)

Phase 2: Clear Corrupted State

bash

Remove potentially corrupted database and instance directories

$ rm -rf ~/.mem0/ $ rm -rf /tmp/mem0/ $ rm -rf ~/.cache/openclaw/plugins/mem0/

Clear any plugin cache

$ openclaw plugin uninstall @mem0/openclaw-mem0 βœ“ Plugin uninstalled

$ openclaw plugin install @mem0/openclaw-mem0 βœ“ Plugin installed (v1.2.3)

Phase 3: Verify Single Registration

Inspect your configuration file to ensure single plugin registration:

yaml

openclaw.config.yaml (BEFORE - problematic)

plugins:

  • name: mem0 package: @mem0/openclaw-mem0 config: api_key: ${MEM0_API_KEY}

Remove duplicate entries below

  • name: mem0-again package: @mem0/openclaw-mem0 config: api_key: ${MEM0_API_KEY}

yaml

openclaw.config.yaml (AFTER - corrected)

plugins:

  • name: mem0 package: @mem0/openclaw-mem0 config: api_key: ${MEM0_API_KEY} db_path: ~/.mem0/vector_store.db # Explicit path

Phase 4: Programmatic Fix for Plugin Code

If modifying the plugin directly, add instance deduplication:

javascript // In your plugin’s index.js let registeredInstance = null; let registrationCount = 0;

export function register(config) { registrationCount++;

if (registrationCount > 1) {
    console.warn(`[mem0] Duplicate registration detected (#${registrationCount}). Reusing existing instance.`);
    return registeredInstance;
}

registeredInstance = new Mem0Plugin(config);
return registeredInstance;

}

Phase 5: Force Synchronous Commit (Temporary Workaround)

If the issue persists, force synchronous writes:

bash

Set environment variable to enable synchronous mode

$ export MEM0_SYNC_WRITE=1 $ export MEM0_COMMIT_INTERVAL_MS=0

Verify environment is set

$ env | grep MEM0 MEM0_SYNC_WRITE=1 MEM0_COMMIT_INTERVAL_MS=0

Phase 6: Manual Database Verification

After implementing fixes, manually verify database state:

bash

Create database directory with proper permissions

$ mkdir -p ~/.mem0 $ chmod 755 ~/.mem0

Verify database creation

$ openclaw run “Test memory” $ sqlite3 ~/.mem0/vector_store.db ‘SELECT count(*) FROM vectors;’

Should now return 1 or more

πŸ§ͺ Verification

Verification Test Suite

Execute the following sequence to confirm the fix:

bash

Step 1: Fresh process initialization

$ openclaw reset –force βœ“ State cleared

Step 2: Verify database initialization

$ sqlite3 ~/.mem0/vector_store.db ‘SELECT name FROM sqlite_master WHERE type=“table”;’ vectors embeddings

Step 3: Add test memory

$ openclaw run “Remember that my birthday is March 15th” βœ“ Memory stored successfully

Step 4: Immediate database verification

$ sqlite3 ~/.mem0/vector_store.db ‘SELECT content FROM vectors LIMIT 1;’ March 15th

Step 5: Cross-instance verification

$ openclaw run “Recall my birthday” Birthday is March 15th

Step 6: Persistent verification (new process)

$ openclaw run “What did I ask you to remember?” Birthday is March 15th

Expected Outputs

CommandExpected Result
sqlite3 ~/.mem0/vector_store.db 'SELECT count(*)'Integer β‰₯ 1
sqlite3 ~/.mem0/vector_store.db '.tables'vectors embeddings
openclaw run "recall"Retrieves previously stored content
openclaw restart && openclaw run "recall"Same result after process restart

Regression Detection Script

bash #!/bin/bash

test-mem0-persistence.sh

DB_PATH="$HOME/.mem0/vector_store.db" TEST_CONTENT=“mem0-persistence-test-$(date +%s)”

Clean start

rm -f “$DB_PATH”

Add memory

openclaw run “Remember: $TEST_CONTENT”

Check database

COUNT=$(sqlite3 “$DB_PATH” “SELECT count(*) FROM vectors WHERE content LIKE ‘%$TEST_CONTENT%’;”)

if [ “$COUNT” -ge 1 ]; then echo “βœ“ PASS: Memory persisted correctly” exit 0 else echo “βœ— FAIL: Memory not found in database” exit 1 fi

⚠️ Common Pitfalls

Pitfall 1: Hidden Duplicate Registrations

Many users include the plugin in both ~/.openclawrc and project-level .openclaw/config.yaml, causing double registration.

Solution: Audit all configuration files: bash $ find ~ -name “.openclaw” -o -name “openclaw*.yaml” 2>/dev/null | xargs grep -l mem0 ~/.openclawrc /projects/myapp/.openclaw/config.yaml

Pitfall 2: Path Tilde Expansion in Different Contexts

The shell expands ~ but the plugin may use os.homedir() or receive raw string without expansion.

javascript // Plugin receives raw path without expansion config.db_path = “~/.mem0/db.sqlite” // WRONG - creates directory named ~

// Correct approach config.db_path = path.join(os.homedir(), ‘.mem0’, ‘db.sqlite’);

Solution: Always use absolute paths in configuration: yaml db_path: /home/username/.mem0/vector_store.db

Pitfall 3: Docker Volume Mount Timing

In containerized environments, the database file may be created inside the container before volume mount, causing writes to an ephemeral layer.

Solution: Pre-create database directory with correct permissions: dockerfile RUN mkdir -p /root/.mem0 && chmod 777 /root/.mem0 VOLUME /root/.mem0

Pitfall 4: Concurrent Write Access

Multiple openclaw processes writing simultaneously can lock the SQLite database.

Solution: Implement write queue or use WAL mode: bash $ sqlite3 ~/.mem0/vector_store.db “PRAGMA journal_mode=WAL;”

Pitfall 5: macOS Gatekeeper Path Isolation

On macOS, sandboxed applications may use ~/Library/Application Support/ instead of ~/.mem0/.

Solution: Set explicit path: bash export MEM0_DB_PATH="$HOME/Library/Application Support/mem0/vector_store.db"

Error CodeDescriptionConnection
#77822Duplicate register() calls causing closure variable desynchronizationPrimary root cause - same underlying mechanism
E2BIGMemory store buffer overflow during bulk migrationRelated - transaction scope issues
SQLITE_BUSYDatabase locked during concurrent writesSymptom of multi-instance writes
ENOTDIRInvalid database path resolutionContributes to ghost writes to wrong location
ECONNREFUSEDPlugin fails to connect to vector storeDownstream failure when DB never initialized
  • #78103 - "mem0 memory_search returns stale results after plugin reload"
  • #77456 - "Plugin singleton pattern broken by hot module replacement"
  • #76912 - "SQLite WAL mode causing read-your-own-writes inconsistency"

Reference Documentation

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.