openclaw - ✅(Solved) Fix [Bug]: QMD backend returns empty `memory_search` results in OpenClaw even when the same content is searchable via `qmd search` [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#58055Fetched 2026-04-08 01:54:29
View on GitHub
Comments
2
Participants
2
Timeline
11
Reactions
0
Author
Participants
Assignees
Timeline (top)
commented ×2cross-referenced ×2labeled ×2referenced ×2

When OpenClaw is configured with memory.backend = "qmd", memory_search may return empty results for content that is already indexed and directly searchable via the QMD CLI.

This suggests the issue is likely in the OpenClaw ↔ QMD integration layer, rather than in QMD indexing/search itself.


Error Message

With memory.backend = "qmd" configured, OpenClaw memory_search returned empty results for queries such as QMD, while direct qmd search "QMD" on the same machine returned hits from indexed memory notes; no user-visible error message was shown in the memory_search response.

Root Cause

When OpenClaw is configured with memory.backend = "qmd", memory_search may return empty results for content that is already indexed and directly searchable via the QMD CLI.

This suggests the issue is likely in the OpenClaw ↔ QMD integration layer, rather than in QMD indexing/search itself.


PR fix notes

PR #58121: fix(qmd): apply han normalization only to lexical sub-queries

Description (problem / solution / changelog)

Fixes #58055. When using the QMD v2 \query\ tool, the raw user query is now passed intact to the vector and hyde sub-queries, while the lexical (BM25) sub-query continues to use the Han-normalized query. This prevents vector/hyde search degradation when searching with Chinese characters.

Summary

Describe the problem and fix in 2–5 bullets:

If this PR fixes a plugin beta-release blocker, title it fix(<plugin-id>): beta blocker - <summary> and link the matching Beta blocker: <plugin-name> - <summary> issue labeled beta-blocker. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.

  • Problem:
  • Why it matters:
  • What changed:
  • What did NOT change (scope boundary):

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #
  • Related #
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause:
  • Missing detection / guardrail:
  • Prior context (git blame, prior PR, issue, or refactor if known):
  • Why this regressed now:
  • If unknown, what was ruled out:

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise write N/A.

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
  • Scenario the test should lock in:
  • Why this is the smallest reliable guardrail:
  • Existing test that already covers this (if any):
  • If no new test is added, why not:

User-visible / Behavior Changes

List user-visible changes (including defaults/config).
If none, write None.

Diagram (if applicable)

For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write N/A.

Before:
[user action] -> [old state]

After:
[user action] -> [new state] -> [result]

Security Impact (required)

  • New permissions/capabilities? (Yes/No)
  • Secrets/tokens handling changed? (Yes/No)
  • New/changed network calls? (Yes/No)
  • Command/tool execution surface changed? (Yes/No)
  • Data access scope changed? (Yes/No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS:
  • Runtime/container:
  • Model/provider:
  • Integration/channel (if any):
  • Relevant config (redacted):

Steps

Expected

Actual

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios:
  • Edge cases checked:
  • What you did not verify:

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No)
  • Config/env changes? (Yes/No)
  • Migration needed? (Yes/No)
  • If yes, exact upgrade steps:

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk:
    • Mitigation:

Fixes #58055

Summary When using the QMD v2 query tool (hybrid search), OpenClaw was applying normalizeHanBm25Query to the entire query before passing it to QMD. This meant that the Han-normalized (space-separated bigrams) query was being sent not just to the lex sub-query, but also to the vec and hyde sub-queries. This aggressively degraded vector similarity matching and semantic expansion for Chinese queries, leading to empty results.

This PR fixes the issue by only applying Han normalization to the lex component, while preserving the raw, original query text for vec and hyde.

Changes

  • In extensions/memory-core/src/memory/qmd-manager.ts, updated buildV2Searches to conditionally normalize the query field based on the search type.
  • Updated qmd-manager.test.ts to assert that the lex query gets normalized while vec and hyde use the original query string.

Changed files

  • extensions/memory-core/src/memory/qmd-manager.test.ts (modified, +23/-7)
  • extensions/memory-core/src/memory/qmd-manager.ts (modified, +5/-3)

Code Example

# QMD backend returns empty `memory_search` results in OpenClaw even when the same content is searchable via `qmd search`

## Summary

When OpenClaw is configured with `memory.backend = "qmd"`, `memory_search` may return empty results for content that is already indexed and directly searchable via the QMD CLI.

This suggests the issue is likely in the OpenClawQMD integration layer, rather than in QMD indexing/search itself.

---

## Environment

- OpenClaw: observed on `2026.3.28`, still reproducible in current setup
- QMD: `2.0.1`
- OS: macOS arm64

Relevant config:

---

Generated QMD collections include both workspace memory and extra wiki docs, e.g.:

- `memory-root-main`
- `memory-alt-main`
- `memory-dir-main`
- `wiki-main`

---

## Problem

OpenClaw `memory_search` returns no results for queries whose matching content is already present in indexed markdown files and can be found by direct QMD CLI search.

---

## What works

Direct QMD CLI search can find the expected content.

Example:

---

This returns hits from existing memory notes, including prior debugging notes about QMD integration.

Another example:

---

This returns hits from `MEMORY.md`.

So in this setup:

- QMD is installed correctly
- the index exists
- the collections are populated
- direct QMD search works

---

## What fails

OpenClaw `memory_search` returns empty results for equivalent queries.

Examples that returned empty results in OpenClaw:

- `QMD`
- queries about previous bug/debug notes related to QMD
- other historical memory items that are known to exist in indexed markdown files

At the same time, the same content can be found via direct `qmd search`.

---

## Expected behavior

If content is indexed by QMD and retrievable with `qmd search`, then OpenClaw `memory_search` using the QMD backend should also surface those results.

---

## Actual behavior

`memory_search` returns empty results, even though the documents are present and directly searchable in QMD.

---

## Why this looks like an integration bug

The evidence suggests QMD itself is functioning:

- `qmd` is installed and runnable
- the generated `index.yml` includes the expected collections
- direct `qmd search` returns relevant hits
- but OpenClaw `memory_search` returns empty results against the same indexed data

This points to a likely issue in one or more of these areas:

- query transformation before invoking QMD
- collection filtering / collection selection
- parsing or mapping QMD results back into OpenClaw memory search results
- backend-specific behavior differences between OpenClaw and direct CLI usage

---

## Related issue: Chinese query normalization may further reduce recall

There may also be a second, related issue for Chinese queries.

OpenClaw appears to normalize Han text queries using bigram splitting. That behavior seems harmful for QMD BM25 search.

In local testing, bypassing this normalization improved Chinese search quality significantly.

So there may be two separate problems:

1. OpenClaw does not reliably surface results that QMD can already find
2. Han/bigram query normalization degrades Chinese recall when using QMD

---

## Reproduction idea

1. Configure OpenClaw with:

---

2. Ensure memory files and/or extra docs are indexed by QMD.

3. Verify direct CLI search works:

---

4. Run OpenClaw `memory_search` with the same query.

5. Compare results.

Observed behavior: direct QMD search returns hits, while OpenClaw `memory_search` may return none.

---

## Impact

This makes the QMD backend unreliable for memory recall inside OpenClaw, even when QMD itself is indexing and searching correctly. In practice, it makes recall appear broken and pushes users back to builtin memory search despite QMD being configured successfully.

---
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

# QMD backend returns empty `memory_search` results in OpenClaw even when the same content is searchable via `qmd search`

## Summary

When OpenClaw is configured with `memory.backend = "qmd"`, `memory_search` may return empty results for content that is already indexed and directly searchable via the QMD CLI.

This suggests the issue is likely in the OpenClaw ↔ QMD integration layer, rather than in QMD indexing/search itself.

---

## Environment

- OpenClaw: observed on `2026.3.28`, still reproducible in current setup
- QMD: `2.0.1`
- OS: macOS arm64

Relevant config:

```json5
{
  memory: {
    backend: "qmd",
    citations: "auto",
    qmd: {
      command: "qmd",
      searchMode: "search",
      includeDefaultMemory: true,
      paths: [
        {
          path: "/Users/.../Documents/wiki",
          name: "wiki",
          pattern: "**/*.md"
        }
      ],
      limits: {
        maxResults: 6,
        timeoutMs: 15000
      }
    }
  }
}

Generated QMD collections include both workspace memory and extra wiki docs, e.g.:

  • memory-root-main
  • memory-alt-main
  • memory-dir-main
  • wiki-main

Problem

OpenClaw memory_search returns no results for queries whose matching content is already present in indexed markdown files and can be found by direct QMD CLI search.


What works

Direct QMD CLI search can find the expected content.

Example:

qmd search "QMD"

This returns hits from existing memory notes, including prior debugging notes about QMD integration.

Another example:

qmd search "用户偏好"

This returns hits from MEMORY.md.

So in this setup:

  • QMD is installed correctly
  • the index exists
  • the collections are populated
  • direct QMD search works

What fails

OpenClaw memory_search returns empty results for equivalent queries.

Examples that returned empty results in OpenClaw:

  • QMD
  • queries about previous bug/debug notes related to QMD
  • other historical memory items that are known to exist in indexed markdown files

At the same time, the same content can be found via direct qmd search.


Expected behavior

If content is indexed by QMD and retrievable with qmd search, then OpenClaw memory_search using the QMD backend should also surface those results.


Actual behavior

memory_search returns empty results, even though the documents are present and directly searchable in QMD.


Why this looks like an integration bug

The evidence suggests QMD itself is functioning:

  • qmd is installed and runnable
  • the generated index.yml includes the expected collections
  • direct qmd search returns relevant hits
  • but OpenClaw memory_search returns empty results against the same indexed data

This points to a likely issue in one or more of these areas:

  • query transformation before invoking QMD
  • collection filtering / collection selection
  • parsing or mapping QMD results back into OpenClaw memory search results
  • backend-specific behavior differences between OpenClaw and direct CLI usage

Related issue: Chinese query normalization may further reduce recall

There may also be a second, related issue for Chinese queries.

OpenClaw appears to normalize Han text queries using bigram splitting. That behavior seems harmful for QMD BM25 search.

In local testing, bypassing this normalization improved Chinese search quality significantly.

So there may be two separate problems:

  1. OpenClaw does not reliably surface results that QMD can already find
  2. Han/bigram query normalization degrades Chinese recall when using QMD

Reproduction idea

  1. Configure OpenClaw with:

    {
      memory: {
        backend: "qmd",
        qmd: {
          searchMode: "search",
          includeDefaultMemory: true,
          paths: [
            { path: "/Users/.../Documents/wiki", name: "wiki", pattern: "**/*.md" }
          ]
        }
      }
    }
  2. Ensure memory files and/or extra docs are indexed by QMD.

  3. Verify direct CLI search works:

    qmd search "QMD"
  4. Run OpenClaw memory_search with the same query.

  5. Compare results.

Observed behavior: direct QMD search returns hits, while OpenClaw memory_search may return none.


Impact

This makes the QMD backend unreliable for memory recall inside OpenClaw, even when QMD itself is indexing and searching correctly. In practice, it makes recall appear broken and pushes users back to builtin memory search despite QMD being configured successfully.


### Steps to reproduce

NOT_ENOUGH_INFO

### Expected behavior

If the same indexed content is retrievable via direct qmd search, OpenClaw memory_search using the QMD backend should return corresponding results instead of an empty result set.

### Actual behavior

With memory.backend = "qmd" configured, OpenClaw memory_search returned empty results for queries such as QMD, while direct qmd search "QMD" on the same machine returned hits from indexed memory notes; no user-visible error message was shown in the memory_search response.

### OpenClaw version

2026.3.28

### Operating system

macOS 26.3.1

### Install method

npm global

### Model

gpt-5.4

### Provider / routing chain

OpenClaw memory_search → OpenClaw QMD memory backend (memory.backend = "qmd") → local qmd CLI / sidecar on the same machine.

### Additional provider/model setup details

_No response_

### Logs, screenshots, and evidence

```shell

Impact and severity

No response

Additional information

No response

extent analysis

Fix Plan

To resolve the issue of OpenClaw's memory_search returning empty results when using the QMD backend, we need to adjust how OpenClaw interacts with QMD. The steps below outline the necessary changes:

  1. Review and Adjust Query Transformation:

    • Ensure that OpenClaw is not modifying the search query in a way that QMD cannot process. This might involve checking for any normalization or transformation logic in OpenClaw that could be altering the query before it's passed to QMD.
  2. Collection Filtering/Selection:

    • Verify that OpenClaw is correctly specifying the collections to search within QMD. This could involve checking the paths and pattern configurations to ensure they match the expected locations and types of files QMD has indexed.
  3. Parsing/Mapping QMD Results:

    • Confirm that OpenClaw is correctly parsing the results returned by QMD. This might involve logging or debugging the response from QMD to ensure it matches the expected format and that OpenClaw is interpreting it correctly.
  4. Backend-Specific Behavior:

    • Investigate if there are any backend-specific behaviors or configurations in OpenClaw that could be affecting how QMD results are handled. This might involve comparing the direct QMD search functionality with how OpenClaw integrates with QMD.
  5. Han Text Query Normalization:

    • For Chinese queries, consider disabling or adjusting the bigram splitting normalization in OpenClaw, as it may be degrading search quality. This could involve adding a configuration option to bypass this normalization for QMD searches.

Example Code Snippets

Given the nature of the issue, the exact code changes will depend on the specifics of OpenClaw's implementation. However, here's a conceptual example of how one might adjust the query handling in OpenClaw to better work with QMD:

// Before calling QMD, ensure the query is not normalized in a way that QMD can't handle
function prepareQueryForQMD(query) {
  // If OpenClaw has a normalization function that's harmful for QMD, bypass it here
  // For example, if OpenClaw normally does bigram splitting on Chinese text, disable it
  if (isChineseQuery(query)) {
    return query; // Return the query as is, without normalization
  }
  // Otherwise, proceed with any necessary transformations that QMD can handle
  return normalizeQueryForQMD(query);
}

// When calling QMD, ensure the correct collections are specified
function searchQMD(query) {
  const collections = getQMDConfig().paths.map(path => path.name);
  const qmdCommand = `qmd search --collections ${collections.join(',')} "${query}"`;
  // Execute the QMD command and parse the results
  const results = executeQMDCommand(qmdCommand);
  return parseQMDResults

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

If the same indexed content is retrievable via direct qmd search, OpenClaw memory_search using the QMD backend should return corresponding results instead of an empty result set.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING