openclaw - 💡(How to fix) Fix Gateway crash on exit: SIGABRT in ggml_metal_rsets_free (node-llama-cpp Metal backend)

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…

Error Message

__pthread_kill → pthread_kill → __abort → abort
→ ggml_abort → ggml_metal_rsets_free → ggml_metal_device_free
→ ~vector<unique_ptr<ggml_metal_device>> → __cxa_finalize_ranges
→ exit → node::Exit

Root Cause

When memorySearch.provider is set to "local", the gateway dynamically imports node-llama-cpp to run a GGUF embedding model (e.g. nomic-embed-text). The Metal backend initializes GPU resources asynchronously — a dispatch queue thread is still in __ggml_metal_rsets_init_block_invoke calling usleep at crash time. When the process exits, the C++ runtime destroys static objects including the Metal device vector, but the resource sets are in an inconsistent state (init not complete), causing an assertion failure in ggml_metal_rsets_free.

This also kills any child processes spawned by the gateway, as they receive SIGHUP when the session leader dies.

Note: This is NOT triggered by user-configured local model providers (which use oMLX or other OpenAI-compatible APIs). It is specifically triggered by the memorySearch.provider: "local" path that loads node-llama-cpp for GGUF embedding inference.

Fix Action

Fix / Workaround

When memorySearch.provider is set to "local", the gateway dynamically imports node-llama-cpp to run a GGUF embedding model (e.g. nomic-embed-text). The Metal backend initializes GPU resources asynchronously — a dispatch queue thread is still in __ggml_metal_rsets_init_block_invoke calling usleep at crash time. When the process exits, the C++ runtime destroys static objects including the Metal device vector, but the resource sets are in an inconsistent state (init not complete), causing an assertion failure in ggml_metal_rsets_free.

  1. User workaround: Uninstall node-llama-cpp (npm uninstall -g node-llama-cpp). OpenClaw will catch ERR_MODULE_NOT_FOUND and report "local embeddings unavailable" without crashing. Memory search will be degraded but the gateway remains stable.

Code Example

__pthread_kill → pthread_kill → __abort → abort
→ ggml_abort → ggml_metal_rsets_free → ggml_metal_device_free
~vector<unique_ptr<ggml_metal_device>> → __cxa_finalize_ranges
→ exit → node::Exit
RAW_BUFFERClick to expand / collapse

Bug Description

The gateway process crashes with SIGABRT during shutdown when memorySearch.provider is set to "local" (using a GGUF embedding model via node-llama-cpp). The crash occurs in the C++ static destructor path when cleaning up node-llama-cpp Metal GPU resources.

Steps to Reproduce

  1. Configure memorySearch.provider: "local" with a GGUF model path in openclaw.json
  2. Start OpenClaw gateway
  3. Gateway loads node-llama-cpp to run the local embedding model for memory search
  4. Gateway exits (normal shutdown or crash recovery)
  5. SIGABRT in ggml_metal_rsets_free

Root Cause

When memorySearch.provider is set to "local", the gateway dynamically imports node-llama-cpp to run a GGUF embedding model (e.g. nomic-embed-text). The Metal backend initializes GPU resources asynchronously — a dispatch queue thread is still in __ggml_metal_rsets_init_block_invoke calling usleep at crash time. When the process exits, the C++ runtime destroys static objects including the Metal device vector, but the resource sets are in an inconsistent state (init not complete), causing an assertion failure in ggml_metal_rsets_free.

This also kills any child processes spawned by the gateway, as they receive SIGHUP when the session leader dies.

Note: This is NOT triggered by user-configured local model providers (which use oMLX or other OpenAI-compatible APIs). It is specifically triggered by the memorySearch.provider: "local" path that loads node-llama-cpp for GGUF embedding inference.

Stack Trace

__pthread_kill → pthread_kill → __abort → abort
→ ggml_abort → ggml_metal_rsets_free → ggml_metal_device_free
→ ~vector<unique_ptr<ggml_metal_device>> → __cxa_finalize_ranges
→ exit → node::Exit

Environment

  • macOS (Apple Silicon)
  • node-llama-cpp v3.18.1 with Metal backend
  • memorySearch.provider: "local" with GGUF embedding model
  • Gateway RSS ~2.5GB at crash time

Suggested Fixes

  1. node-llama-cpp side (recommended): ggml_metal_rsets_free should gracefully skip cleanup when initialization is not complete, instead of aborting the entire process. This is a resource lifecycle management issue in the Metal backend.

  2. OpenClaw side: Add graceful degradation for node-llama-cpp Metal backend failures during shutdown — catch the abort and log a warning instead of crashing the entire gateway.

  3. User workaround: Uninstall node-llama-cpp (npm uninstall -g node-llama-cpp). OpenClaw will catch ERR_MODULE_NOT_FOUND and report "local embeddings unavailable" without crashing. Memory search will be degraded but the gateway remains stable.

Impact

  • Gateway crashes intermittently on exit when local embedding provider is configured
  • All child processes (exec tool subprocesses) are killed when gateway crashes
  • Users lose in-progress work from spawned CLI tools

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Gateway crash on exit: SIGABRT in ggml_metal_rsets_free (node-llama-cpp Metal backend)