<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Regression on FixClaw</title>
        <link>https://fixclaw.dev/tags/regression/</link>
        <description>Recent content in Regression on FixClaw</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Mon, 01 Jan 0001 00:00:00 +0000</lastBuildDate><atom:link href="https://fixclaw.dev/tags/regression/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>Chrome Exits Immediately After OpenClaw Launch on Windows 11 - CDP Never Binds</title>
            <link>https://fixclaw.dev/troubleshooting/chrome-exits-immediately-after-openclaw-launch-on-windows-11---cdp-never-binds/</link>
            <pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate>
            <guid>https://fixclaw.dev/troubleshooting/chrome-exits-immediately-after-openclaw-launch-on-windows-11---cdp-never-binds/</guid>
            <description>&lt;h2 id=&#34;symptom&#34;&gt;Symptom&#xA;&lt;/h2&gt;&lt;p&gt;On Windows 11, when attempting to start a managed browser using OpenClaw, Chrome launches successfully with the correct CDP flags and a PID is returned, but Chrome terminates within seconds before CDP can bind to port 18800.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Observed behavior:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;OpenClaw spawns Chrome and logs the PID:&#xA;[browser/chrome] 🦞 openclaw browser started (custom) profile &amp;ldquo;openclaw&amp;rdquo; on 127.0.0.1:18800 (pid 9592)&lt;/li&gt;&#xA;&lt;li&gt;Chrome process immediately exits&lt;/li&gt;&#xA;&lt;li&gt;Connection attempts fail:&#xA;[ws] ⇄ res ✗ browser.request 10462ms errorCode=UNAVAILABLE errorMessage=Error: Chrome CDP websocket for profile &amp;ldquo;openclaw&amp;rdquo; is not reachable after start.&lt;/li&gt;&#xA;&lt;li&gt;Verification commands confirm Chrome is not running:&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Get-Process -Name chrome&lt;/code&gt; → no processes&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Test-NetConnection 127.0.0.1 -Port 18800&lt;/code&gt; → &lt;code&gt;TcpTestSucceeded: False&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;curl http://127.0.0.1:18800/json/version&lt;/code&gt; → connection refused&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;strong&gt;Expected behavior:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Chrome remains running after launch&lt;/li&gt;&#xA;&lt;li&gt;CDP websocket becomes available on port 18800&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;openclaw browser status&lt;/code&gt; shows &lt;code&gt;running: true&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;root-cause-analysis&#34;&gt;Root Cause Analysis&#xA;&lt;/h2&gt;&lt;p&gt;The issue is specifically in &lt;strong&gt;OpenClaw&amp;rsquo;s process lifecycle management after spawn&lt;/strong&gt;. This conclusion is supported by the following evidence:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Factor Tested&lt;/th&gt;&#xA;          &lt;th&gt;Result&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Chrome executable&lt;/td&gt;&#xA;          &lt;td&gt;Works when launched manually&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;All Chrome arguments&lt;/td&gt;&#xA;          &lt;td&gt;Identical args work when run manually in PowerShell&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;User data directory&lt;/td&gt;&#xA;          &lt;td&gt;Valid and functional when used outside OpenClaw&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Sandbox settings&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;noSandbox: true&lt;/code&gt; makes no difference&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Port 18800 availability&lt;/td&gt;&#xA;          &lt;td&gt;Port is not blocked; works with manual launch&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;CDP configuration&lt;/td&gt;&#xA;          &lt;td&gt;Functions correctly when Chrome runs independently&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;&lt;strong&gt;Key finding:&lt;/strong&gt; When the exact same Chrome executable with identical arguments is launched manually from PowerShell, Chrome:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Stays alive&lt;/li&gt;&#xA;&lt;li&gt;Serves CDP on port 18800 (&lt;code&gt;TcpTestSucceeded: True&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;Returns HTTP 200 on &lt;code&gt;curl http://127.0.0.1:18800/json/version&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This definitively rules out Chrome, arguments, user-data-dir, sandbox configuration, and port issues. The problem originates from how OpenClaw manages the spawned Chrome process after launch, likely in one of these areas:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Process inheritance&lt;/strong&gt; - OpenClaw may be holding file handles or stdin/stdout pipes that cause Chrome to terminate&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Process group handling&lt;/strong&gt; - Windows process group termination may be killing the child process&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Parent process exit propagation&lt;/strong&gt; - Exit signals may be incorrectly propagating to the child Chrome process&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Environment variable handling&lt;/strong&gt; - Missing or incorrect environment variables passed to Chrome&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;solution&#34;&gt;Solution&#xA;&lt;/h2&gt;&lt;p&gt;This is a regression in OpenClaw&amp;rsquo;s browser process spawning mechanism. Until a permanent fix is released, the following workarounds can be used:&lt;/p&gt;&#xA;&lt;h3 id=&#34;workaround-1-use-attachonly-mode&#34;&gt;Workaround 1: Use attachOnly Mode&#xA;&lt;/h3&gt;&lt;p&gt;Configure a separate browser profile that connects to an already-running Chrome instance:&lt;/p&gt;&#xA;&lt;p&gt;{&#xA;&amp;ldquo;browser&amp;rdquo;: {&#xA;&amp;ldquo;profiles&amp;rdquo;: {&#xA;&amp;ldquo;manual&amp;rdquo;: {&#xA;&amp;ldquo;attachOnly&amp;rdquo;: true,&#xA;&amp;ldquo;cdpPort&amp;rdquo;: 18800&#xA;}&#xA;}&#xA;}&#xA;}&lt;/p&gt;&#xA;&lt;p&gt;Then launch Chrome manually with debugging enabled:&#xA;&amp;amp; &amp;ldquo;C:\Program Files\Google\Chrome\Application\chrome.exe&amp;rdquo; &amp;ndash;remote-debugging-port=18800 &amp;ndash;user-data-dir=&amp;ldquo;C:\Users\Me.openclaw\browser\openclaw\user-data&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Finally, connect using OpenClaw:&#xA;openclaw browser &amp;ndash;browser-profile manual start&lt;/p&gt;&#xA;&lt;h3 id=&#34;workaround-2-launch-chrome-before-openclaw-operations&#34;&gt;Workaround 2: Launch Chrome Before OpenClaw Operations&#xA;&lt;/h3&gt;&lt;p&gt;Start Chrome manually before performing any OpenClaw browser operations, then use the &lt;code&gt;attachOnly&lt;/code&gt; approach described above.&lt;/p&gt;&#xA;&lt;h2 id=&#34;prevention&#34;&gt;Prevention&#xA;&lt;/h2&gt;&lt;p&gt;To prevent this issue from affecting your workflow:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Monitor browser status&lt;/strong&gt; before running commands that require CDP:&#xA;openclaw browser status&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Implement health checks&lt;/strong&gt; in automation scripts:&lt;/p&gt;&#xA;&lt;h1 id=&#34;wait-for-browser-to-be-ready&#34;&gt;Wait for browser to be ready&#xA;&lt;/h1&gt;&lt;p&gt;until curl -s http://127.0.0.1:18800/json/version &amp;gt; /dev/null; do&#xA;sleep 0.5&#xA;done&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Keep detailed logs&lt;/strong&gt; of browser launches to quickly identify when this regression affects your environment&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Test browser launches&lt;/strong&gt; after OpenClaw updates, as this is identified as a regression&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;additional-information&#34;&gt;Additional Information&#xA;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Environment Details:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;OpenClaw version: 2026.4.11 (769908e)&lt;/li&gt;&#xA;&lt;li&gt;Operating System: Windows 11&lt;/li&gt;&#xA;&lt;li&gt;Chrome version: 146.0.7680.178&lt;/li&gt;&#xA;&lt;li&gt;Chrome path: &lt;code&gt;C:\Program Files\Google\Chrome\Application\chrome.exe&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Working Chrome Launch Arguments:&lt;/strong&gt;&#xA;&amp;ldquo;C:\Program Files\Google\Chrome\Application\chrome.exe&amp;rdquo;&#xA;&amp;ndash;remote-debugging-port=18800&#xA;&amp;ndash;user-data-dir=C:\Users\Me.openclaw\browser\openclaw\user-data&#xA;&amp;ndash;no-first-run&#xA;&amp;ndash;no-default-browser-check&#xA;&amp;ndash;disable-sync&#xA;&amp;ndash;disable-background-networking&#xA;&amp;ndash;disable-component-update&#xA;&amp;ndash;disable-features=Translate,MediaRouter&#xA;&amp;ndash;disable-session-crashed-bubble&#xA;&amp;ndash;hide-crash-restore-bubble&#xA;&amp;ndash;password-store=basic&#xA;&amp;ndash;no-sandbox&#xA;&amp;ndash;disable-setuid-sandbox&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Browser Control Service Status:&lt;/strong&gt;&#xA;The browser control service itself is healthy and listening correctly:&#xA;[browser/server] Browser control listening on http://127.0.0.1:18791/ (auth=token)&lt;/p&gt;&#xA;&lt;h2 id=&#34;sources&#34;&gt;Sources&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/openclaw/openclaw/issues/65521&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitHub Issue #65521&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item><item>
            <title>TTS Voice Note Delivery Regression in v2026.4.9</title>
            <link>https://fixclaw.dev/troubleshooting/tts-voice-note-delivery-regression-in-v202649/</link>
            <pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate>
            <guid>https://fixclaw.dev/troubleshooting/tts-voice-note-delivery-regression-in-v202649/</guid>
            <description>&lt;h2 id=&#34;symptom&#34;&gt;Symptom&#xA;&lt;/h2&gt;&lt;p&gt;After upgrading to OpenClaw v2026.4.9, TTS (text-to-speech) voice note delivery is completely broken:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Outbound TTS (Voice Notes from Assistant):&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;User sends a text message to the assistant via Telegram&lt;/li&gt;&#xA;&lt;li&gt;Assistant calls &lt;code&gt;tts&lt;/code&gt; tool with &lt;code&gt;channel: &amp;quot;telegram&amp;quot;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Tool returns &lt;code&gt;Generated audio reply&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Expected:&lt;/strong&gt; Voice note delivered to user&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Actual:&lt;/strong&gt; Only text response received; no voice note&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;strong&gt;Inbound STT (Voice Notes to Assistant):&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;User sends a voice note to the assistant via Telegram&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Expected:&lt;/strong&gt; Audio transcribed and processed&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Actual:&lt;/strong&gt; No transcription; &lt;code&gt;pdf&lt;/code&gt; tool fails with &amp;ldquo;Expected PDF but got audio/ogg&amp;rdquo;, &lt;code&gt;image&lt;/code&gt; tool fails with &amp;ldquo;Unsupported media type: audio&amp;rdquo;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;strong&gt;Evidence from logs:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Gateway logs show &lt;strong&gt;no TTS-related events&lt;/strong&gt; after the &lt;code&gt;tts&lt;/code&gt; tool call&lt;/li&gt;&#xA;&lt;li&gt;No &lt;code&gt;sendVoice&lt;/code&gt; API calls are made to Telegram&lt;/li&gt;&#xA;&lt;li&gt;Text messages continue to work correctly (proving Telegram connectivity is intact)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Historical logs show the last &lt;code&gt;sendVoice&lt;/code&gt; attempts failing:&#xA;telegram sendVoice failed: Network request for &amp;lsquo;sendVoice&amp;rsquo; failed!&#xA;telegram final reply failed: HttpError: Network request for &amp;lsquo;sendVoice&amp;rsquo; failed!&lt;/p&gt;&#xA;&lt;h2 id=&#34;root-cause-analysis&#34;&gt;Root Cause Analysis&#xA;&lt;/h2&gt;&lt;p&gt;The investigation reveals that the &lt;strong&gt;tool→channel delivery handoff is broken&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;ElevenLabs API is functioning correctly&lt;/strong&gt; — Audio files are successfully generated by the &lt;code&gt;tts&lt;/code&gt; tool&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Tool reports success&lt;/strong&gt; — &lt;code&gt;Generated audio reply&lt;/code&gt; message confirms audio creation&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Broken handoff&lt;/strong&gt; — The audio file is &lt;strong&gt;never passed to the gateway&amp;rsquo;s Telegram channel&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No &lt;code&gt;sendVoice&lt;/code&gt; call&lt;/strong&gt; — No Telegram API call is initiated to deliver the voice note&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Regression introduced in v2026.4.9&lt;/strong&gt; — The change from pre-v2026.4.9 behavior (corrupted delivery) to v2026.4.9 (no delivery) indicates a code regression in how the &lt;code&gt;tts&lt;/code&gt; tool result is handled by the channel delivery layer&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;strong&gt;Pre-v2026.4.9 behavior:&lt;/strong&gt; TTS generated audio but it was corrupted (appearing as long strings of exclamation marks in webchat).&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;v2026.4.9 behavior:&lt;/strong&gt; TTS generates audio but delivers nothing at all.&lt;/p&gt;&#xA;&lt;p&gt;The shift from &amp;ldquo;corrupted delivery&amp;rdquo; to &amp;ldquo;no delivery&amp;rdquo; suggests the audio file handling code path was either removed or broken during the v2026.4.9 update.&lt;/p&gt;&#xA;&lt;h2 id=&#34;solution&#34;&gt;Solution&#xA;&lt;/h2&gt;&lt;p&gt;This is a &lt;strong&gt;code regression bug&lt;/strong&gt; that requires a fix in the OpenClaw codebase. The following steps are needed:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Restore the &lt;code&gt;sendVoice&lt;/code&gt; call path&lt;/strong&gt; in the channel delivery layer — Ensure the audio file generated by the &lt;code&gt;tts&lt;/code&gt; tool is properly passed to &lt;code&gt;sendVoice&lt;/code&gt; for Telegram delivery&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Verify audio file handling&lt;/strong&gt; — Confirm the audio file path/content is correctly transferred between the tool result handler and the Telegram sender&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Add integration tests&lt;/strong&gt; for TTS→Telegram delivery to prevent future regressions&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Fix STT handling&lt;/strong&gt; — Ensure inbound voice notes are properly recognized and routed to the STT pipeline instead of being rejected as unsupported media types&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;strong&gt;Temporary Workaround:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Users may attempt to use the &lt;code&gt;message&lt;/code&gt; tool with explicit media path for file attachment (untested)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;prevention&#34;&gt;Prevention&#xA;&lt;/h2&gt;&lt;p&gt;To prevent similar regressions in future releases:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Add automated TTS integration tests&lt;/strong&gt; — Create end-to-end tests that verify voice notes are actually delivered to Telegram (not just that the tool reports success)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Test the full delivery pipeline&lt;/strong&gt; — Ensure both generation AND delivery are tested together, not in isolation&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Monitor channel delivery layer changes&lt;/strong&gt; — Any modifications to how tool results are processed should include regression testing for media delivery&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Log delivery confirmation&lt;/strong&gt; — Add explicit logging when &lt;code&gt;sendVoice&lt;/code&gt; is called to make failures more visible in logs&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Multi-channel testing&lt;/strong&gt; — Test TTS functionality when multiple channels (Telegram + webchat) are active simultaneously, as session handling issues may compound the problem&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;additional-information&#34;&gt;Additional Information&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Affected versions:&lt;/strong&gt; OpenClaw v2026.4.9 (updated 2026-04-09) and potentially later versions&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Previous working behavior:&lt;/strong&gt; Pre-v2026.4.9 had corrupted audio delivery&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;TTS Provider tested:&lt;/strong&gt; ElevenLabs (&lt;code&gt;eleven_multilingual_v2&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Channel:&lt;/strong&gt; Telegram&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Network connectivity:&lt;/strong&gt; Confirmed working (text messages deliver successfully)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;API credentials:&lt;/strong&gt; ElevenLabs credentials verified functional via manual API calls&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Multi-session factor:&lt;/strong&gt; Session handling issues may contribute when Telegram and webchat are used simultaneously&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;sources&#34;&gt;Sources&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/openclaw/openclaw/issues/64272&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitHub Issue #64272&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item><item>
            <title>Agent-Sent TTS Audio Not Delivered via Webchat</title>
            <link>https://fixclaw.dev/troubleshooting/agent-sent-tts-audio-not-delivered-via-webchat/</link>
            <pubDate>Sun, 08 Mar 2026 00:00:00 +0000</pubDate>
            <guid>https://fixclaw.dev/troubleshooting/agent-sent-tts-audio-not-delivered-via-webchat/</guid>
            <description>&lt;h2 id=&#34;symptom&#34;&gt;Symptom&#xA;&lt;/h2&gt;&lt;p&gt;Agent-initiated TTS audio fails to reach webchat clients through two different paths:&lt;/p&gt;&#xA;&lt;h3 id=&#34;path-1-tts-tool&#34;&gt;Path 1: &lt;code&gt;tts&lt;/code&gt; Tool&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;Agent calls the &lt;code&gt;tts&lt;/code&gt; tool with text&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;textToSpeech()&lt;/code&gt; succeeds, audio file is created on disk (confirmed valid MP3)&lt;/li&gt;&#xA;&lt;li&gt;Agent replies with &lt;code&gt;NO_REPLY&lt;/code&gt; (as instructed by tool description)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No audio is delivered to the webchat client&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;path-2-tts-tags&#34;&gt;Path 2: &lt;code&gt;[[tts]]&lt;/code&gt; Tags&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;Agent includes &lt;code&gt;[[tts]]&lt;/code&gt; or &lt;code&gt;[[tts:text]]...[[/tts:text]]&lt;/code&gt; tags in a reply&lt;/li&gt;&#xA;&lt;li&gt;During streaming, raw tags are visible in the UI (not processed)&lt;/li&gt;&#xA;&lt;li&gt;After the run completes, &lt;strong&gt;no audio is delivered&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;Tags may remain visible as raw text in the final message&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;working-path-tts-audio&#34;&gt;Working Path: &lt;code&gt;/tts audio&lt;/code&gt;&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;User runs &lt;code&gt;/tts audio Hello world&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Audio is generated and delivered correctly on both webchat and Telegram&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;root-cause-analysis&#34;&gt;Root Cause Analysis&#xA;&lt;/h2&gt;&lt;p&gt;This is a &lt;strong&gt;regression&lt;/strong&gt; caused by a mismatch between where audio is emitted and where it is processed in the reply delivery pipeline.&lt;/p&gt;&#xA;&lt;h3 id=&#34;root-cause-1-block-vs-final-payload-mismatch-tts-tool-path&#34;&gt;Root Cause 1: Block vs Final Payload Mismatch (&lt;code&gt;tts&lt;/code&gt; tool path)&#xA;&lt;/h3&gt;&lt;p&gt;The issue lies in the disconnect between how pending tool media is emitted and how webchat processes replies:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Emission side&lt;/strong&gt; (&lt;code&gt;pi-embedded&lt;/code&gt; — &lt;code&gt;handleAgentEnd&lt;/code&gt; → &lt;code&gt;flushPendingMediaAndChannel&lt;/code&gt;):&#xA;const pendingToolMediaReply = consumePendingToolMediaReply(ctx.state);&#xA;if (pendingToolMediaReply &amp;amp;&amp;amp; hasAssistantVisibleReply(pendingToolMediaReply))&#xA;ctx.emitBlockReply(pendingToolMediaReply);  // ← emitted as &amp;ldquo;block&amp;rdquo; kind&#xA;Audio is emitted as a &lt;strong&gt;&amp;ldquo;block&amp;rdquo;&lt;/strong&gt; kind reply and queued in &lt;code&gt;state.pendingToolMediaUrls&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Processing side&lt;/strong&gt; (&lt;code&gt;server.impl&lt;/code&gt; — webchat dispatch post-processing):&#xA;const finalPayloads = deliveredReplies&#xA;.filter((entry) =&amp;gt; entry.kind === &amp;ldquo;final&amp;rdquo;)  // ← blocks excluded&#xA;.map((entry) =&amp;gt; entry.payload);&#xA;const audioBlocks = buildWebchatAudioContentBlocksFromReplyPayloads(finalPayloads);&#xA;Webchat &lt;strong&gt;only&lt;/strong&gt; extracts audio from &amp;ldquo;final&amp;rdquo; kind payloads, completely ignoring block payloads.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The block payload containing the audio file path is collected by the deliver callback but never processed for audio extraction. The audio file sits on disk, unused.&lt;/p&gt;&#xA;&lt;h3 id=&#34;root-cause-2-auto-tts-mode-skips-block-replies-tts-tag-path&#34;&gt;Root Cause 2: Auto-TTS Mode Skips Block Replies (&lt;code&gt;[[tts]]&lt;/code&gt; tag path)&#xA;&lt;/h3&gt;&lt;p&gt;The auto-TTS mode defaults to &lt;code&gt;&amp;quot;final&amp;quot;&lt;/code&gt;, which causes block-kind replies to be skipped entirely:&lt;/p&gt;&#xA;&lt;p&gt;// speech-core/runtime-api.js — maybeApplyTtsToPayload&#xA;if ((config.mode ?? &amp;ldquo;final&amp;rdquo;) === &amp;ldquo;final&amp;rdquo; &amp;amp;&amp;amp; params.kind &amp;amp;&amp;amp; params.kind !== &amp;ldquo;final&amp;rdquo;)&#xA;return nextPayload;  // ← blocks skip TTS processing entirely&lt;/p&gt;&#xA;&lt;p&gt;For streaming models, agent text is delivered incrementally as block replies, so &lt;code&gt;[[tts]]&lt;/code&gt; tags pass through unprocessed during streaming.&lt;/p&gt;&#xA;&lt;p&gt;A synthetic fallback exists in dispatch that attempts TTS on accumulated block text after the run completes:&lt;/p&gt;&#xA;&lt;p&gt;// dispatch — after embedded run&#xA;if (resolveConfiguredTtsMode(cfg) === &amp;ldquo;final&amp;rdquo; &amp;amp;&amp;amp; replies.length === 0&#xA;&amp;amp;&amp;amp; blockCount &amp;gt; 0 &amp;amp;&amp;amp; accumulatedBlockText.trim()) {&#xA;const ttsSyntheticReply = await maybeApplyTtsToReplyPayload({&#xA;payload: { text: accumulatedBlockText },&#xA;cfg, channel: ttsChannel, kind: &amp;ldquo;final&amp;rdquo;, &amp;hellip;&#xA;});&lt;/p&gt;&#xA;&lt;p&gt;However, this fallback &lt;strong&gt;only fires when &lt;code&gt;replies.length === 0&lt;/code&gt;&lt;/strong&gt; (no final replies returned from the embedded run). Whether the embedded run returns final replies depends on the model/provider implementation, making this path unreliable.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;solution&#34;&gt;Solution&#xA;&lt;/h2&gt;&lt;h3 id=&#34;for-tts-tool-path&#34;&gt;For &lt;code&gt;tts&lt;/code&gt; tool path:&#xA;&lt;/h3&gt;&lt;p&gt;Modify &lt;code&gt;server.impl&lt;/code&gt; to include block-kind payloads when extracting audio content blocks:&lt;/p&gt;&#xA;&lt;p&gt;// server.impl — webchat dispatch post-processing&#xA;// Before (broken):&#xA;const finalPayloads = deliveredReplies&#xA;.filter((entry) =&amp;gt; entry.kind === &amp;ldquo;final&amp;rdquo;)&#xA;.map((entry) =&amp;gt; entry.payload);&lt;/p&gt;&#xA;&lt;p&gt;// After (fixed):&#xA;const allPayloads = deliveredReplies&#xA;.map((entry) =&amp;gt; entry.payload);&#xA;const audioBlocks = buildWebchatAudioContentBlocksFromReplyPayloads(allPayloads);&lt;/p&gt;&#xA;&lt;h3 id=&#34;for-tts-tag-path&#34;&gt;For &lt;code&gt;[[tts]]&lt;/code&gt; tag path:&#xA;&lt;/h3&gt;&lt;p&gt;Modify &lt;code&gt;speech-core/runtime-api.js&lt;/code&gt; to process block-kind replies when in streaming mode:&lt;/p&gt;&#xA;&lt;p&gt;// speech-core/runtime-api.js — maybeApplyTtsToPayload&#xA;// Before (broken):&#xA;if ((config.mode ?? &amp;ldquo;final&amp;rdquo;) === &amp;ldquo;final&amp;rdquo; &amp;amp;&amp;amp; params.kind &amp;amp;&amp;amp; params.kind !== &amp;ldquo;final&amp;rdquo;)&#xA;return nextPayload;&lt;/p&gt;&#xA;&lt;p&gt;// After (fixed):&#xA;// Allow block-kind processing when streaming is active&#xA;if (params.kind === &amp;ldquo;block&amp;rdquo; &amp;amp;&amp;amp; !config.streamingMode) {&#xA;return nextPayload;&#xA;}&lt;/p&gt;&#xA;&lt;p&gt;Alternatively, update the synthetic fallback in dispatch to be more robust regardless of whether final replies are present.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;prevention&#34;&gt;Prevention&#xA;&lt;/h2&gt;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Add integration tests&lt;/strong&gt; that verify audio delivery for all TTS paths (tool, tags, and slash command) across all channels (webchat, Telegram).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Introduce a unified payload extraction function&lt;/strong&gt; that processes both block and final kinds for audio content, preventing similar mismatches in the future.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Add QA checklist&lt;/strong&gt; for TTS-related changes requiring verification that audio artifacts flow correctly from generation → emission → processing → delivery.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Document payload kind semantics&lt;/strong&gt; clearly, especially the distinction between &amp;ldquo;block&amp;rdquo; and &amp;ldquo;final&amp;rdquo; kinds and which processing stages expect which kinds.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;additional-information&#34;&gt;Additional Information&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Affected version&lt;/strong&gt;: 2026.3.7/2026.3.8&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Operating system&lt;/strong&gt;: Debian GNU/Linux 12 (bookworm)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Install method&lt;/strong&gt;: Docker&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Affected models&lt;/strong&gt;: claude-opus-4.6, gpt-codex-5.4&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Affected channels&lt;/strong&gt;: web, telegram&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Severity&lt;/strong&gt;: Complete feature disablement (100% impact on agent-sent TTS)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Frequency&lt;/strong&gt;: Always reproducible&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The &lt;code&gt;/tts audio&lt;/code&gt; slash command works correctly because it bypasses the embedded handler entirely and delivers audio through the standard final-payload path.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;sources&#34;&gt;Sources&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/openclaw/openclaw/issues/63033&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitHub Issue #63033&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item><item>
            <title>GGML Metal Assertion Crash on Apple Silicon During Shutdown with Local Embeddings</title>
            <link>https://fixclaw.dev/troubleshooting/ggml-metal-assertion-crash-on-apple-silicon-during-shutdown-with-local-embedding/</link>
            <pubDate>Mon, 02 Mar 2026 00:00:00 +0000</pubDate>
            <guid>https://fixclaw.dev/troubleshooting/ggml-metal-assertion-crash-on-apple-silicon-during-shutdown-with-local-embedding/</guid>
            <description>&lt;h2 id=&#34;symptom&#34;&gt;Symptom&#xA;&lt;/h2&gt;&lt;p&gt;When running OpenClaw on Apple Silicon (M-series Macs) with local embeddings configured for memory search, the application crashes during shutdown (e.g., when receiving Ctrl+C / SIGINT or during autoupdate). The crash produces a GGML Metal assertion failure visible in the logs:&lt;/p&gt;&#xA;&lt;p&gt;/Users/runner/work/node-llama-cpp/node-llama-cpp/llama/llama.cpp/ggml/src/ggml-metal/ggml-metal-device.m:608: GGML_ASSERT([rsets-&amp;gt;data count] == 0) failed&lt;/p&gt;&#xA;&lt;p&gt;The full stack trace shows the crash originates from &lt;code&gt;ggml_metal_device_free&lt;/code&gt; being called during process exit, indicating that Metal resources were not properly released before shutdown.&lt;/p&gt;&#xA;&lt;p&gt;Additionally, the following error may appear in the logs before the crash:&lt;/p&gt;&#xA;&lt;p&gt;Unhandled promise rejection: AssertionError [ERR_ASSERTION]: Reached illegal state! IPV4 address change from defined to undefined!&lt;/p&gt;&#xA;&lt;p&gt;This network-related error is a secondary symptom that occurs during the same shutdown sequence.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Affected Configurations:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;macOS&lt;/code&gt; (all versions with Apple Silicon)&lt;/li&gt;&#xA;&lt;li&gt;Local embeddings provider enabled via &lt;code&gt;node-llama-cpp&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Example configuration:&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&amp;ldquo;agents&amp;rdquo;: {&#xA;&amp;ldquo;defaults&amp;rdquo;: {&#xA;&amp;ldquo;memorySearch&amp;rdquo;: {&#xA;&amp;ldquo;provider&amp;rdquo;: &amp;ldquo;local&amp;rdquo;,&#xA;&amp;ldquo;local&amp;rdquo;: {&#xA;&amp;ldquo;modelPath&amp;rdquo;: &amp;ldquo;/path/to/embeddinggemma-&amp;hellip;gguf&amp;rdquo;&#xA;}&#xA;}&#xA;}&#xA;}&lt;/p&gt;&#xA;&lt;h2 id=&#34;root-cause-analysis&#34;&gt;Root Cause Analysis&#xA;&lt;/h2&gt;&lt;p&gt;The crash is caused by a &lt;strong&gt;resource leak&lt;/strong&gt; in the interaction between OpenClaw and the &lt;code&gt;node-llama-cpp&lt;/code&gt; library when using Metal GPU acceleration on Apple Silicon.&lt;/p&gt;&#xA;&lt;h3 id=&#34;technical-details&#34;&gt;Technical Details&#xA;&lt;/h3&gt;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Embedding Context Lifecycle&lt;/strong&gt;: When local embeddings are used, &lt;code&gt;node-llama-cpp&lt;/code&gt; creates embedding contexts that hold Metal GPU resources (textures, buffers, and command queues managed by GGML Metal).&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Missing Disposal on Shutdown&lt;/strong&gt;: During normal process exit (SIGINT, SIGTERM, or autoupdate restart), these embedding contexts are not explicitly disposed before the Node.js event loop terminates. This leaves Metal resources in an active state.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;GGML Metal Unload Assertion&lt;/strong&gt;: When the process exits, &lt;code&gt;llama.cpp&lt;/code&gt;&amp;rsquo;s &lt;code&gt;ggml_metal_device_free()&lt;/code&gt; function runs during the &lt;code&gt;atexit()&lt;/code&gt; phase. This function asserts that all Metal resource sets (&lt;code&gt;rsets-&amp;gt;data&lt;/code&gt;) have been released. Since the embedding contexts were not disposed, this assertion fails:&#xA;GGML_ASSERT([rsets-&amp;gt;data count] == 0)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Regression Indicator&lt;/strong&gt;: This issue is classified as a regression because the functionality worked in previous versions, suggesting a change in either OpenClaw&amp;rsquo;s shutdown handling, &lt;code&gt;node-llama-cpp&lt;/code&gt;&amp;rsquo;s behavior, or a combination of both.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;call-chain&#34;&gt;Call Chain&#xA;&lt;/h3&gt;&lt;p&gt;process.exit()&#xA;→ exit() [libsystem_c.dylib]&#xA;→ __cxa_finalize_ranges()&#xA;→ ggml_metal_device_free() [libggml-metal.so]&#xA;→ GGML_ASSERT([rsets-&amp;gt;data count] == 0)  // FAILS HERE&lt;/p&gt;&#xA;&lt;h2 id=&#34;solution&#34;&gt;Solution&#xA;&lt;/h2&gt;&lt;p&gt;Implement a cleanup patch that tracks embedding contexts and explicitly disposes them before process exit.&lt;/p&gt;&#xA;&lt;h3 id=&#34;step-1-create-the-cleanup-patch-file&#34;&gt;Step 1: Create the Cleanup Patch File&#xA;&lt;/h3&gt;&lt;p&gt;Create a new file at &lt;code&gt;src/memory/local-cleanup-patch.ts&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;p&gt;import { getLlama } from &amp;rsquo;node-llama-cpp&#39;;&lt;/p&gt;&#xA;&lt;p&gt;const trackedContexts: any[] = [];&lt;/p&gt;&#xA;&lt;p&gt;const originalCreate = getLlama.prototype.createEmbeddingContext;&#xA;getLlama.prototype.createEmbeddingContext = async function (&amp;hellip;args: any[]) {&#xA;const ctx = await originalCreate.apply(this, args);&#xA;trackedContexts.push(ctx);&#xA;return ctx;&#xA;};&lt;/p&gt;&#xA;&lt;p&gt;async function cleanup() {&#xA;if (trackedContexts.length === 0) return;&#xA;console.log(&lt;code&gt;[cleanup] Disposing ${trackedContexts.length} embedding context(s)&lt;/code&gt;);&#xA;for (const ctx of trackedContexts) {&#xA;if (ctx?.dispose) {&#xA;await ctx.dispose().catch(e =&amp;gt; console.warn(&amp;rsquo;[cleanup] Dispose failed:&amp;rsquo;, e));&#xA;}&#xA;}&#xA;trackedContexts.length = 0;&#xA;}&lt;/p&gt;&#xA;&lt;p&gt;process.once(&amp;lsquo;SIGINT&amp;rsquo;, cleanup);&#xA;process.once(&amp;lsquo;SIGTERM&amp;rsquo;, cleanup);&#xA;process.on(&amp;lsquo;beforeExit&amp;rsquo;, cleanup);&lt;/p&gt;&#xA;&lt;p&gt;export { cleanup };&lt;/p&gt;&#xA;&lt;h3 id=&#34;step-2-modify-the-import-function&#34;&gt;Step 2: Modify the Import Function&#xA;&lt;/h3&gt;&lt;p&gt;Update &lt;code&gt;src/memory/node-llama.ts&lt;/code&gt; to automatically apply the cleanup patch when local embeddings are used:&lt;/p&gt;&#xA;&lt;p&gt;export async function importNodeLlamaCpp() {&#xA;// Automatically apply our shutdown fix when local embeddings are used&#xA;await import(&amp;rsquo;./local-cleanup-patch&amp;rsquo;);&#xA;return import(&amp;ldquo;node-llama-cpp&amp;rdquo;);&#xA;}&lt;/p&gt;&#xA;&lt;h3 id=&#34;step-3-verify-the-fix&#34;&gt;Step 3: Verify the Fix&#xA;&lt;/h3&gt;&lt;p&gt;After implementing the changes:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Restart the OpenClaw service&lt;/li&gt;&#xA;&lt;li&gt;Trigger an autoupdate or send SIGINT (Ctrl+C)&lt;/li&gt;&#xA;&lt;li&gt;Confirm the application shuts down gracefully without GGML Metal assertion failures&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;prevention&#34;&gt;Prevention&#xA;&lt;/h2&gt;&lt;p&gt;To prevent this issue from recurring:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Always Test Shutdown Sequences&lt;/strong&gt;: When integrating libraries that manage native resources (GPU, CUDA, Metal), always test graceful shutdown scenarios including SIGINT, SIGTERM, and forced exits.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Implement Resource Tracking&lt;/strong&gt;: For any async resource allocation (embedding contexts, model instances), implement tracking mechanisms that can be flushed during shutdown.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Register Cleanup Handlers Early&lt;/strong&gt;: Register shutdown cleanup handlers (&lt;code&gt;process.once(&#39;SIGINT&#39;)&lt;/code&gt;, &lt;code&gt;process.once(&#39;SIGTERM&#39;)&lt;/code&gt;, &lt;code&gt;process.on(&#39;beforeExit&#39;)&lt;/code&gt;) as early as possible in the application lifecycle.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Use Graceful Shutdown Patterns&lt;/strong&gt;: Implement a unified shutdown manager that coordinates cleanup across all subsystems, ensuring native resources are released before the event loop terminates.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Add Regression Tests&lt;/strong&gt;: Consider adding automated tests that verify graceful shutdown behavior on all supported platforms, particularly Apple Silicon with Metal GPU acceleration.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;additional-information&#34;&gt;Additional Information&#xA;&lt;/h2&gt;&lt;h3 id=&#34;temporary-workarounds&#34;&gt;Temporary Workarounds&#xA;&lt;/h3&gt;&lt;p&gt;If the fix cannot be applied immediately, use one of the following workarounds to disable Metal GPU acceleration:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Option 1&lt;/strong&gt;: Disable GPU layers entirely&#xA;export NODE_LLAMA_CPP_GPU_LAYERS=0&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Option 2&lt;/strong&gt;: Disable Metal specifically (version-dependent)&#xA;export NODE_LLAMA_CPP_METAL=false&lt;/p&gt;&#xA;&lt;p&gt;Note: These workarounds will reduce embedding performance but prevent the crash.&lt;/p&gt;&#xA;&lt;h3 id=&#34;affected-versions&#34;&gt;Affected Versions&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;OpenClaw &lt;strong&gt;2026.3.1&lt;/strong&gt; is confirmed affected&lt;/li&gt;&#xA;&lt;li&gt;Earlier versions may also be affected depending on the &lt;code&gt;node-llama-cpp&lt;/code&gt; version in use&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;related-dependencies&#34;&gt;Related Dependencies&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;node-llama-cpp&lt;/code&gt;: Embedding context management&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;ggml-metal&lt;/code&gt; (llama.cpp): Metal GPU resource management&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;@homebridge/ciao&lt;/code&gt;: mDNS/network management (secondary error source during shutdown)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;external-references&#34;&gt;External References&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;GGML Metal device cleanup: &lt;a class=&#34;link&#34; href=&#34;https://github.com/ggml-org/llama.cpp/pull/17869&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;llama.cpp PR #17869&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Setting &lt;code&gt;GGML_BACKTRACE_LLDB&lt;/code&gt; may cause native MacOS Terminal.app to crash; use with caution&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;sources&#34;&gt;Sources&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/openclaw/openclaw/issues/32452&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitHub Issue #32452&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item></channel>
</rss>
