Skip to content

fix(memory): warn when qmd binary is missing#57467

Merged
vincentkoc merged 5 commits intomainfrom
fix/qmd-binary-warning
Mar 30, 2026
Merged

fix(memory): warn when qmd binary is missing#57467
vincentkoc merged 5 commits intomainfrom
fix/qmd-binary-warning

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

Summary

  • Problem: memory.backend=qmd still silently downgraded to builtin memory when the configured qmd command was missing or not spawnable.
  • Why it matters: users only saw degraded behavior or a generic fallback log, and openclaw doctor skipped the exact missing-binary diagnosis entirely.
  • What changed: added a small QMD binary availability probe, used it in runtime fallback and doctor, and added regression coverage for both paths.
  • What did NOT change (scope boundary): this does not change QMD indexing/search semantics or fallback-to-builtin behavior; it only makes the failure explicit and actionable.

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

Root Cause / Regression History (if applicable)

  • Root cause: the QMD backend path attempted manager creation directly and treated missing-binary startup failures as a generic runtime fallback, while doctor-memory-search explicitly returned early for the QMD backend and never probed command availability.
  • Missing detection / guardrail: there was no small binary-availability probe shared between runtime fallback and doctor diagnostics.
  • Prior context (git blame, prior PR, issue, or refactor if known): fix(memory): detect missing qmd binary and warn user #41053 identified the same user pain on older code paths.
  • Why this regressed now: the current QMD manager boundary still preserved the silent downgrade pattern after the earlier code layout changed.
  • If unknown, what was ruled out: N/A.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
  • packages/memory-host-sdk/src/host/qmd-process.test.ts
  • extensions/memory-core/src/memory/search-manager.test.ts
  • src/commands/doctor-memory-search.test.ts
  • Scenario the test should lock in: a missing qmd binary should produce an explicit doctor warning and immediate builtin fallback without attempting QMD manager creation.
  • Why this is the smallest reliable guardrail: the bug spans the process probe, backend selector, and doctor surface; these three tests cover the full user-visible seam without needing a full external QMD install.
  • Existing test that already covers this (if any): none.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • openclaw doctor now warns when memory.backend=qmd is configured but the qmd binary cannot be started.
  • Runtime fallback logs now explain that the configured QMD command is unavailable before falling back to builtin memory.

Diagram (if applicable)

Before:
[user enables qmd] -> [qmd command missing] -> [generic init failure] -> [silent builtin downgrade]

After:
[user enables qmd] -> [qmd command missing] -> [explicit probe failure] -> [doctor/runtime warning] -> [builtin fallback]

Security Impact (required)

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

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 / pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): memory.backend=qmd, default memory.qmd.command=qmd

Steps

  1. Configure memory.backend=qmd with no installed qmd binary.
  2. Run openclaw doctor or trigger memory manager startup.
  3. Observe warning behavior.

Expected

  • Users get a clear missing-binary warning plus actionable next steps.

Actual

  • Doctor returned early for QMD with no warning, and runtime fallback only emitted a generic downgrade log.

Evidence

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

Human Verification (required)

  • Verified scenarios: missing-binary process probe, immediate builtin fallback without QMD manager creation, doctor warning text with install/config recovery guidance.
  • Edge cases checked: spawn success path, spawn error path, command resolution failure path covered by the shared probe.
  • What you did not verify: live manual run against an actual missing global qmd install outside the test harness.

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.

Compatibility / Migration

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

Risks and Mitigations

  • Risk: the availability probe could misclassify a very slow command startup as unavailable.
    • Mitigation: the helper uses a short timeout only for spawn-level availability and keeps the runtime fallback intact.

@openclaw-barnacle openclaw-barnacle bot added extensions: memory-core Extension: memory-core commands Command implementations size: M labels Mar 30, 2026
@vincentkoc vincentkoc self-assigned this Mar 30, 2026
@openclaw-barnacle openclaw-barnacle bot added the maintainer Maintainer-authored PR label Mar 30, 2026
@vincentkoc vincentkoc marked this pull request as ready for review March 30, 2026 04:27
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4e7dae0a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 30, 2026

Greptile Summary

This PR adds an explicit qmd binary availability probe to fix a long-standing silent-downgrade bug where a missing qmd binary would cause the QMD memory backend to fall back to builtin memory with no actionable user feedback. The change introduces checkQmdBinaryAvailability in qmd-process.ts, wires it into both the runtime fallback path (search-manager.ts) and the openclaw doctor command, and adds regression tests for all three surfaces.

Key findings:

  • Performance regression in search-manager.ts: checkQmdBinaryAvailability is called before the QMD_MANAGER_CACHE lookup, so every call to getMemorySearchManager — including cache hits — forks a new qmd process. The cache exists precisely to avoid this overhead on the hot search path. The probe should only run after confirming no cached manager exists.
  • Edge-case fragility in qmd-process.ts: The close event handler returns available: true unconditionally. The settled guard correctly prevents a double-resolve in the ENOENT path today, but if close fired before error on a future Node.js version the function would misreport a failed spawn as available. Guarding the close handler with a didSpawn flag would eliminate this risk.
  • The doctor warning text, recovery suggestions, and new test coverage are well-structured and clearly actionable.
  • The stale JSDoc comment on noteMemorySearchHealth (\"config-only, no network calls\") should be updated since the function now spawns a probe process.

Confidence Score: 4/5

Safe to merge after resolving the cache-ordering regression; all other findings are minor.

One P1 finding: the binary probe in search-manager.ts runs before the cache check, adding subprocess-fork overhead to every QMD memory search even when a manager is already cached. This is a clear performance regression on the hot path introduced by this PR. The remaining issues (close-handler race, stale comment) are P2. The correctness of the new warning and fallback logic is sound.

extensions/memory-core/src/memory/search-manager.ts — move the QMD_MANAGER_CACHE lookup above the checkQmdBinaryAvailability call.

Important Files Changed

Filename Overview
extensions/memory-core/src/memory/search-manager.ts Binary availability probe inserted before cache check, causing a subprocess fork on every memory search request even when a manager is already cached — a performance regression on the hot path.
packages/memory-host-sdk/src/host/qmd-process.ts New checkQmdBinaryAvailability helper correctly probes binary via spawn/error/close events with a timeout and settled guard; minor edge-case fragility in the close handler if it fires before error on some platforms.
packages/memory-host-sdk/src/host/qmd-process.test.ts Tests refactored to module-level scope and extended with two new cases covering successful spawn and ENOENT failure paths for checkQmdBinaryAvailability; existing resolveCliSpawnInvocation coverage preserved.
packages/memory-host-sdk/src/engine-qmd.ts Exports checkQmdBinaryAvailability from the public SDK surface alongside the existing resolveCliSpawnInvocation and runCliCommand.
src/commands/doctor-memory-search.ts Doctor command now probes QMD binary availability and emits an actionable warning with install/config recovery steps when the binary is missing; JSDoc comment is now stale.
src/commands/doctor-memory-search.test.ts New test verifies that the doctor note fires with expected content (error string and install command) when checkQmdBinaryAvailability returns unavailable; existing QMD happy-path test updated to supply the qmd config field.
extensions/memory-core/src/memory/search-manager.test.ts New regression test confirms that a missing binary causes immediate builtin fallback without calling QmdMemoryManager.create; mock wiring for checkQmdBinaryAvailability correctly added.
CHANGELOG.md Changelog entry added for the QMD binary-missing warning fix.

Comments Outside Diff (1)

  1. src/commands/doctor-memory-search.ts, line 24-27 (link)

    P2 Stale JSDoc comment — function now spawns a process

    The JSDoc says "config-only, no network calls", but after this PR the function also calls checkQmdBinaryAvailability, which forks a child process. The comment should be updated to reflect the new behaviour so callers aren't surprised.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/commands/doctor-memory-search.ts
    Line: 24-27
    
    Comment:
    **Stale JSDoc comment — function now spawns a process**
    
    The JSDoc says _"config-only, no network calls"_, but after this PR the function also calls `checkQmdBinaryAvailability`, which forks a child process. The comment should be updated to reflect the new behaviour so callers aren't surprised.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/memory-core/src/memory/search-manager.ts
Line: 51-65

Comment:
**Binary probe runs before cache check — subprocess spawned on every cache hit**

`checkQmdBinaryAvailability` is called unconditionally at line 51 before the `QMD_MANAGER_CACHE` lookup at line 63. This means every call to `getMemorySearchManager` — including the hot path where a manager is already cached — forks a new `qmd` child process, waits for the OS `spawn` event, and kills it, before ever consulting the cache.

The entire purpose of `QMD_MANAGER_CACHE` was to avoid repeated startup overhead on the critical search path. Before this PR, a cache hit returned in microseconds. Now every cache hit pays a subprocess-fork round-trip (typically 10–50 ms on Linux/macOS) plus the probe's 2-second worst-case timeout if the binary is slow to respond.

The fix is to move the cache check above the probe call so cached managers are returned immediately without any subprocess work, and the probe only runs when a new manager must actually be created.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/memory-host-sdk/src/host/qmd-process.ts
Line: 89-91

Comment:
**`close` handler can race with the `error` path on some platforms**

In Node.js, when a binary cannot be found (ENOENT), the `error` event fires first and then the `close` event fires with `null` for both code and signal. The `settled` guard correctly prevents `finish({ available: true })` from being called a second time, so there is no *correctness* bug in the current code.

However, if the `close` event somehow fires first (theoretically possible in edge-cases of future Node.js versions or non-standard libuv implementations), it would incorrectly report `available: true` for a process that actually failed to start.

A safer pattern is to only treat `close` as success when it comes *after* a confirmed `spawn`:

```typescript
let didSpawn = false;
child.once("spawn", () => {
  didSpawn = true;
  child.kill();
  finish({ available: true });
});
child.once("close", () => {
  // Only use close as a success signal if spawn already fired.
  if (didSpawn) {
    finish({ available: true });
  }
});
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/commands/doctor-memory-search.ts
Line: 24-27

Comment:
**Stale JSDoc comment — function now spawns a process**

The JSDoc says _"config-only, no network calls"_, but after this PR the function also calls `checkQmdBinaryAvailability`, which forks a child process. The comment should be updated to reflect the new behaviour so callers aren't surprised.

```suggestion
 * Check whether memory search has a usable embedding provider.
 * Runs as part of `openclaw doctor` — config-only checks where possible;
 * may spawn a short-lived probe process when `memory.backend=qmd` to verify
 * the configured `qmd` binary is available.
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Merge branch 'main' into fix/qmd-binary-..." | Re-trigger Greptile

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e28672c52

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@vincentkoc vincentkoc merged commit 8623c28 into main Mar 30, 2026
9 checks passed
@vincentkoc vincentkoc deleted the fix/qmd-binary-warning branch March 30, 2026 04:44
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57b6f516cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +58 to +61
const qmdCheck = await checkQmdBinaryAvailability({
command: backendConfig.qmd?.command ?? "qmd",
env: process.env,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Probe QMD from agent workspace in doctor checks

The new openclaw doctor probe calls checkQmdBinaryAvailability without a cwd, so relative memory.qmd.command values are resolved against the shell’s current directory instead of the configured agent workspace. In runtime, QMD commands are executed from the agent workspace (see extensions/memory-core/src/memory/search-manager.ts), so a valid config like ./bin/qmd can be reported as “binary missing” when users run doctor outside that workspace, producing a false warning and incorrect remediation guidance.

Useful? React with 👍 / 👎.

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 30, 2026

🔒 Aisle Security Analysis

We found 4 potential security issue(s) in this PR:

# Severity Title
1 🟠 High openclaw doctor executes configured memory.qmd.command when backend is qmd
2 🟠 High Windows untrusted search path execution when probing/spawning qmd from workspace/current directory
3 🟡 Medium Terminal escape/log injection via unsanitized qmd spawn error messages in user-facing output
4 🔵 Low Availability probe may delay for timeout when child emits close before spawn
Vulnerabilities

1. 🟠 openclaw doctor executes configured memory.qmd.command when backend is qmd

Property Value
Severity High
CWE CWE-78
Location src/commands/doctor-memory-search.ts:57-61

Description

noteMemorySearchHealth (run as part of openclaw doctor) spawns a subprocess to probe the QMD backend by executing the configured command:

  • Input: backendConfig.qmd?.command ultimately comes from user/project configuration (memory.qmd.command).
  • Sink: checkQmdBinaryAvailability calls child_process.spawn() with that command.
  • Impact: if an attacker can influence the config in a repo/workspace (or via config precedence), convincing a user/CI/editor integration to run openclaw doctor becomes an arbitrary local code execution primitive (run attacker-chosen executable).

Vulnerable code:

const qmdCheck = await checkQmdBinaryAvailability({
  command: backendConfig.qmd?.command ?? "qmd",
  env: process.env,
});

Notes:

  • memory.qmd.command is not restricted to an allowlist or absolute path, and the probe executes the command (even though it is short-lived).
  • The implementation does not appear to use shell: true (good), and args are not taken from config; however, selecting the executable alone is sufficient for code execution if config is untrusted.

Recommendation

Avoid executing configurable commands during diagnostics, or gate it behind explicit user intent.

Recommended mitigations (pick one or combine):

  1. Do not spawn at all in doctor; instead perform a non-executing check (best):

    • Require memory.qmd.command to be an absolute path and validate it with fs.existsSync() / access checks, or
    • Resolve via PATH lookup without executing (platform-specific), then show guidance.
  2. Require explicit opt-in to run probes:

    • Add a flag like openclaw doctor --allow-spawn (default false) and skip probe otherwise.
  3. Constrain the command:

    • Only allow the literal qmd (or an absolute path), reject anything else.

Example (absolute-path only, no spawn):

import fs from "node:fs";
import path from "node:path";

const cmd = backendConfig.qmd?.command ?? "qmd";
if (!path.isAbsolute(cmd) || !fs.existsSync(cmd)) {
  note("QMD is configured but memory.qmd.command must be an existing absolute path; skipping execution in doctor.");
  return;
}

If you still need to execute, run only a fixed binary name and fixed args, and clearly warn users that doctor may execute local binaries.


2. 🟠 Windows untrusted search path execution when probing/spawning qmd from workspace/current directory

Property Value
Severity High
CWE CWE-426
Location packages/memory-host-sdk/src/host/qmd-process.ts:36-74

Description

The QMD availability probe can execute an attacker-controlled binary on Windows due to untrusted search path behavior.

  • checkQmdBinaryAvailability() spawns params.command with a caller-controlled cwd (agent workspace) or defaults to process.cwd().
  • When memory.qmd.command is configured as a bare name like qmd (the documented default), resolveWindowsExecutablePath() only resolves to an absolute path if it finds a match in PATH. If not found, it returns the bare name.
  • On Windows, CreateProcess search order can include the current working directory. That means if a user opens an untrusted repository/workspace and the process cwd (or agent workspace dir) contains a malicious qmd.exe/qmd.cmd, it may be executed during the probe.

This turns a configuration/health check (and later QMD backend spawning which uses the same resolution path and cwd) into potential arbitrary code execution when operating in attacker-controlled directories.

Vulnerable code:

const child = spawn(spawnInvocation.command, spawnInvocation.argv, {
  env: params.env,
  cwd: params.cwd ?? process.cwd(),
  shell: spawnInvocation.shell,
  windowsHide: spawnInvocation.windowsHide,
  stdio: "ignore",
});

Recommendation

Prevent Windows current-directory binary hijacking by ensuring the spawned program is resolved to an absolute path (or fails) before calling spawn, and avoid using attacker-controlled cwd for binary resolution.

Suggested fix (fail closed if the command cannot be resolved via PATH when it is not an explicit path):

import path from "node:path";

function isExplicitPath(cmd: string) {
  return cmd.includes("/") || cmd.includes("\\") || path.isAbsolute(cmd);
}

export async function checkQmdBinaryAvailability(params: { command: string; env: NodeJS.ProcessEnv; cwd?: string; timeoutMs?: number; }) {
  const spawnInvocation = resolveCliSpawnInvocation({
    command: params.command,
    args: [],
    env: params.env,
    packageName: "qmd",
  });// On Windows, ensure resolution didn't fall back to a bare name.
  if (process.platform === "win32" && !isExplicitPath(params.command) && !path.isAbsolute(spawnInvocation.command)) {
    return { available: false, error: `qmd not found on PATH: ${params.command}` };
  }// Optionally, use a neutral cwd for the probe.
  const safeCwd = process.platform === "win32" ? process.env.SystemRoot ?? process.cwd() : (params.cwd ?? process.cwd());

  const child = spawn(spawnInvocation.command, spawnInvocation.argv, {
    env: params.env,
    cwd: safeCwd,
    shell: spawnInvocation.shell,
    windowsHide: spawnInvocation.windowsHide,
    stdio: "ignore",
  });
}

Additionally:

  • Consider requiring memory.qmd.command to be an absolute path on Windows, or explicitly document/validate it.
  • Apply the same absolute-resolution requirement in the actual QMD execution path (e.g., where runCliCommand is used) to avoid executing from an untrusted workspace.

3. 🟡 Terminal escape/log injection via unsanitized qmd spawn error messages in user-facing output

Property Value
Severity Medium
CWE CWE-150
Location src/commands/doctor-memory-search.ts:63-67

Description

The CLI prints and logs raw error strings originating from process spawn failures without neutralizing control characters.

  • checkQmdBinaryAvailability() returns err.message verbatim (from Node spawn/validation errors).
  • These messages are interpolated directly into:
    • note(...) output shown to users during openclaw doctor.
    • log.warn(...) messages in the memory search manager.
  • If an attacker can influence the configured memory.qmd.command (e.g., via a shared/malicious config file) or otherwise trigger crafted error text, they may inject terminal control sequences (ANSI escapes) or forge multi-line log entries.
  • Even without an active attacker, the raw error often includes sensitive local details such as absolute paths, usernames, or platform-specific spawn diagnostics.

Vulnerable code (user-facing note):

qmdCheck.error ? `Probe error: ${qmdCheck.error}` : null,

Vulnerable code (logging):

`... falling back to builtin: ${qmdBinary.error ?? "unknown error"}`

Recommendation

Neutralize control characters before writing untrusted strings to the terminal/logs, and consider hiding detailed spawn diagnostics unless --verbose is enabled.

Example mitigation:

function sanitizeForTerminal(input: string): string {// Remove C0/C1 control chars and ESC to prevent ANSI/terminal injection
  return input.replace(/[\u0000-\u001F\u007F-\u009F\u001B]/g, "");
}

const safeErr = qmdCheck.error ? sanitizeForTerminal(qmdCheck.error) : undefined;
note(
  [
    `QMD memory backend is configured, but the qmd binary could not be started (${cmd}).`,
    safeErr ? `Probe error: ${safeErr}` : null,
  ].filter(Boolean).join("\n"),
  "Memory search",
);

Also apply similar sanitization to log.warn(...) messages that include qmdBinary.error / thrown err.message.


4. 🔵 Availability probe may delay for timeout when child emits close before spawn

Property Value
Severity Low
CWE CWE-400
Location packages/memory-host-sdk/src/host/qmd-process.ts:91-96

Description

checkQmdBinaryAvailability can take the full timeout (default 2000ms) to resolve in certain event orderings because the close handler returns early when didSpawn is false.

  • The promise is only settled on spawn, error, or the timeout.
  • If the ChildProcess emits close without a preceding spawn (and without an error), the handler returns early and does not resolve.
  • In that case the function will only resolve when the timeout fires, causing an avoidable delay.
  • If this probe is invoked frequently (e.g., per agent or per command) and an attacker/user can influence conditions that trigger this event ordering (bad/slow binary path, platform quirks), it can be used to create a controllable slowdown (application-layer DoS).

Vulnerable code:

child.once("close", () => {
  if (!didSpawn) {
    return;
  }
  finish({ available: true });
});

Recommendation

Ensure the promise always settles on close, and treat close before spawn as an immediate failure (or at least as unavailable) rather than waiting for the timeout.

Example:

child.once("close", (code, signal) => {
  if (!didSpawn) {
    finish({
      available: false,
      error: `qmd exited before spawn (code=${code}, signal=${signal ?? ""})`,
    });
    return;
  }
  finish({ available: true });
});

Additionally, defensively wrap child.kill(...) in try/catch in the timeout handler to avoid unexpected exceptions preventing settlement.


Analyzed PR: #57467 at commit 57b6f51

Last updated on: 2026-03-30T09:55:31Z

alexjiang1 pushed a commit to alexjiang1/openclaw that referenced this pull request Mar 31, 2026
* fix(memory): warn when qmd binary is missing

* fix(memory): avoid probing cached qmd managers

* docs(memory): clarify qmd doctor probe behavior

* fix(memory): probe qmd from agent workspace
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
* fix(memory): warn when qmd binary is missing

* fix(memory): avoid probing cached qmd managers

* docs(memory): clarify qmd doctor probe behavior

* fix(memory): probe qmd from agent workspace
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
* fix(memory): warn when qmd binary is missing

* fix(memory): avoid probing cached qmd managers

* docs(memory): clarify qmd doctor probe behavior

* fix(memory): probe qmd from agent workspace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations extensions: memory-core Extension: memory-core maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: QMD binary missing — "spawn qmd ENOENT" on macOS ARM

1 participant