Skip to content

[Bug]: Active Memory reports status=empty for normal no-useful-memory results, making recall appear broken #79812

Description

@jsompis

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Active Memory frequently reports user-visible status=empty even when the system is functioning but simply found no useful memory for the current turn.

Example user-facing output:

🧩 Active Memory: status=empty elapsed=9.2s query=recent

This is misleading because empty looks like a failure or broken recall state. In practice, some cases are normal “no relevant memory needed/found” outcomes and should not be surfaced as a hard empty/failure status.

There were two related issues observed:

  1. A previous bug where Active Memory could terminate as status=empty after a single zero-hit memory_search, before the recall agent had a chance to retry or broaden the query.
  2. A later issue where the zero-hit terminal path was already patched, but normal NONE / “no useful memory” results were still being recorded and surfaced as status=empty, causing the user to see empty 100% of the time in some conditions.

Steps to reproduce

  1. Enable Active Memory in an OpenClaw session.

  2. Send normal short messages that do not necessarily need memory recall, such as acknowledgements or generic follow-ups.

  3. Observe the Active Memory status line.

  4. In affected cases, Active Memory reports:

    🧩 Active Memory: status=empty elapsed=... query=recent
    
  5. Repeat across multiple turns.

  6. The user may see status=empty repeatedly, even though this may represent a normal “no useful memory” result rather than a recall failure.

Expected behavior

Active Memory should distinguish between:

  • real failure: recall agent crashed, timed out, failed to query, or encountered an exception
  • zero-hit search: first query found no matches but retry/broadening may still be possible
  • normal no-useful-memory result: recall ran successfully but determined no memory should be injected
  • successful recall: relevant memory found and injected

For normal no-useful-memory results, the user-facing status should not be empty.

Possible better statuses:

status=ok
status=no_context
status=no_relevant_memory
status=skipped

The important part is that a normal no-useful-memory outcome should not look like a broken memory system.

Actual behavior

Active Memory reports:

status=empty

for cases that appear to be normal no-useful-memory / NONE outcomes.

This makes users think Active Memory is broken or failing every turn.

In earlier behavior, Active Memory also appeared to end too early after a single zero-hit memory search instead of allowing retry/broaden behavior.

OpenClaw version

2026.5.7

Operating system

macOS 26.4.1

Install method

npm global

Model

openai-codex/gpt-5.5

Provider / routing chain

openclaw->openai-codex/gpt-5.5

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Impact and severity

Severity: Medium to High for user trust and debuggability.

Impact:

  • Users believe Active Memory is broken because status=empty appears repeatedly.
  • Normal no-context outcomes are indistinguishable from failure.
  • Debugging becomes harder because empty conflates multiple states.
  • The memory system appears unreliable even when it may be operating correctly.
  • In chat UX, repeated status=empty is noisy and misleading.

Requested fix

Please separate Active Memory status classification into clearer states.

Suggested behavior:

  1. If recall fails due to exception/timeout:

    status=failed
    
  2. If memory search returns zero hits but the recall process can retry or broaden:

    status=retrying
    

    or keep it internal until final classification.

  3. If recall completes successfully but decides no memory is useful:

    status=no_relevant_memory
    

    or:

    status=ok
    
  4. Reserve:

    status=empty
    

    only for true empty/unavailable memory state, not normal NONE decisions.

  5. Avoid surfacing status=empty to users for ordinary short messages where no memory is needed.

Possible implementation areas to inspect:

  • Active Memory terminal watcher
  • classification of recall-agent output such as NONE
  • zero-hit memory_search handling
  • status mapping from internal recall result to user-facing status line
  • query generation for short acknowledgement messages such as recent

Additional information

Environment

  • OpenClaw version observed locally: 2026.5.7 (eeef486)
  • Active Memory extension/plugin enabled
  • Memory backend: QMD lexical search mode
  • Channel: Telegram direct chat
  • Host: macOS / Darwin arm64 26.4.1
  • User-facing symptom appeared in normal chat turns

Relevant local runtime file investigated:

dist/extensions/active-memory/index.js

A local hotfix/patch file was maintained for this behavior:

active-memory-zero-hit-hotfix.patch
--- a/dist/extensions/active-memory/index.js
+++ b/dist/extensions/active-memory/index.js
@@ -671,7 +671,10 @@
        return singleLine.length > MAX_LOG_VALUE_CHARS ? `${singleLine.slice(0, MAX_LOG_VALUE_CHARS)}...` : singleLine;
 }
 function shouldCacheResult(result) {
-       return result.status === "ok" || result.status === "empty";
+       // Local hotfix: a no-match Active Memory run must not be sticky. Caching
+       // `empty` makes one conservative/NONE recall answer repeat for identical
+       // recent-context queries and looks like Active Memory is 100% empty.
+       return result.status === "ok" && Boolean(result.summary && result.summary.length > 0);
 }
 function resolveStatusUpdateAgentId(ctx) {
        const explicit = ctx.agentId?.trim();
@@ -854,11 +857,15 @@
        const details = asRecord(message.details);
        const debug = extractActiveMemorySearchDebugFromSessionRecord(value);
        const results = Array.isArray(details?.results) ? details.results : void 0;
-       const unavailable = details?.disabled === true || Boolean(debug?.warning) || Boolean(debug?.error) || Boolean(details?.error);
+       const unavailable = details?.disabled === true || Boolean(debug?.error) || Boolean(details?.error);
        const debugHits = typeof debug?.hits === "number" && Number.isFinite(debug.hits) ? debug.hits : void 0;
        const zeroHitSearch = results !== void 0 ? results.length === 0 : debugHits === 0;
-       if (unavailable || zeroHitSearch) return {
-               status: "empty",
+       // Local hotfix: do not terminate active-memory on a single zero-hit search.
+       // The recall agent prompt may retry with a broader query, especially for QMD
+       // lexical mode where multi-term queries can be too strict. Keep terminal
+       // short-circuit only for unavailable/error cases.
+       if (unavailable) return {
+               status: "unavailable",
                searchDebug: debug
        };
 }
@@ -1510,7 +1517,10 @@
                        summary,
                        searchDebug
                } : {
-                       status: "empty",
+                       // Local hotfix: NONE/no useful memory is a successful recall pass, not a
+                       // terminal empty/error state. Keep the hidden prompt empty, but avoid the
+                       // noisy user-facing status=empty line after a normal no-match decision.
+                       status: "ok",
                        elapsedMs: Date.now() - startedAt,
                        summary: null,
                        searchDebug

Logs, screenshots, and evidence

Observed user-facing status:

🧩 Active Memory: status=empty elapsed=9.2s query=recent

Prior investigation found:

  • QMD lexical search mode itself was not the main issue.
  • Missing vector embeddings were not the main issue for this symptom.
  • The old zero-hit terminal path caused the run to end as empty too early.
  • After the zero-hit path was patched, a newer issue remained: normal NONE / no-useful-memory outcomes were still classified as empty.
  • This caused the user-facing status to look like a failure even when recall had successfully decided there was nothing useful to inject.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingbug:behaviorIncorrect behavior without a crash

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions