Skip to content

memory_search blocks foreground tool calls by awaiting sync on search miss #98520

Description

@Mingke77777

Summary

memory_search can block the foreground tool call for minutes when the first search returns zero results, because it synchronously awaits manager.sync({ reason: "search", force: true }) before returning to the agent.

This makes a normal "no results" search miss behave like a full memory indexing/sync operation inside the user-facing tool call deadline.

Observed Behavior

On OpenClaw 2026.6.11, a direct dynamic memory_search call for a query with no hits repeatedly timed out in the foreground tool layer:

memory_search timed out after 15s
OpenClaw dynamic tool call timed out after 90000ms while running tool memory_search

Queries with existing hits returned normally once the miss path was avoided.

After locally disabling the foreground sync-on-miss branch in the generated dist/tools-*.js, both cases returned immediately:

memory_search("warmup") -> [] in ~28ms
memory_search("fruit juice farm noon appmagic") -> 3 results in ~17ms

Root Cause

The memory_search tool currently performs a synchronous full sync when rawResults.length === 0:

if (rawResults.length === 0 && activeMemory.manager.sync) {
  await activeMemory.manager.sync({
    reason: "search",
    force: true
  });
  rawResults = await activeMemory.manager.search(query, searchOptions);
}

For local QMD-backed memory stores, sync may involve filesystem scanning, vector updates, embedding calls, local model initialization, or other expensive work. That work should not block a foreground agent tool call, especially under 15s/90s watchdogs.

Expected Behavior

A search miss should return an empty result set promptly.

If OpenClaw wants to improve freshness after a miss, it should schedule a background sync that does not block the user-facing tool call.

Suggested Fix

Add a config flag, defaulting to non-blocking behavior:

{
  "memorySearch": {
    "syncOnMiss": false
  }
}

Possible implementation:

if (rawResults.length === 0 && activeMemory.manager.sync && syncOnMiss) {
  if (syncOnMiss === "background") {
    activeMemory.manager.sync({ reason: "search", force: true }).catch((err) => {
      log.warn({ err }, "memory_search background sync-on-miss failed");
    });
  } else {
    await activeMemory.manager.sync({ reason: "search", force: true });
    rawResults = await activeMemory.manager.search(query, searchOptions);
  }
}

Recommended default: false or "background", not foreground await.

Why This Matters

Agent tool calls are latency-sensitive. A no-hit memory search is a normal outcome and should not trigger an expensive synchronous maintenance path. In production assistants, this can make memory appear broken even though the index and search backend are healthy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.impact:crash-loopCrash, hang, restart loop, or process-level availability failure.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions