openclaw - ✅(Solved) Fix Hardcoded `/api` prefix in Signal `httpUrl` causes 404 with signal-cli-rest-api [1 pull requests, 1 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#73553Fetched 2026-04-29 06:18:19
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
0
Timeline (top)
labeled ×2closed ×1commented ×1cross-referenced ×1

The Signal channel gateway prepends a hardcoded /api prefix to the httpUrl when connecting to an external daemon, causing 404 errors with standard signal-cli-rest-api implementations.

Error Message

OpenClaw internal logs: error | gateway/channels/signal | Signal SSE stream error: Error: Signal SSE failed (404 Not Found) info | gateway/channels/signal | Signal SSE connection lost, reconnecting in 10s...

Docker (signal-cli-rest-api) logs: [GIN] 2026/04/28 - 11:58:24 | 404 | 603ns | 172.17.0.1 | GET "/api/v1/events?account=%2B41266732337" [GIN] 2026/04/28 - 11:58:27 | 404 | 1.039µs | 172.17.0.1 | GET "/api/v1/check"

Root Cause

  • Affected users: All users using an external Signal daemon via httpUrl.
  • Severity: Blocks workflow (Channel unusable).
  • Frequency: Always.
  • Consequence: Signal messages cannot be received or sent because the SSE stream fails to initialize.

Fix Action

Fix / Workaround

The issue seems to be a hardcoded assumption that the REST API is served under /api. Standard signal-cli-rest-api (the most common implementation) serves directly under the root /v1. A temporary workaround requires a reverse proxy to strip the /api prefix.

PR fix notes

PR #16085: Signal: add REST API support for containerized deployments

Description (problem / solution / changelog)

AI-assisted: Built with Claude (Copilot CLI). Fully tested. Human reviewed and verified.

Summary

  • Problem: Signal channel only works with direct signal-cli (JSON-RPC + SSE). The official bbernhard/signal-cli-rest-api Docker image uses REST + WebSocket endpoints that don't exist in the current implementation, causing 404 errors and failed connections.
  • Why it matters: Containerized Signal deployments are broken — users must install signal-cli directly on the host (requires Java, complex setup).
  • What changed: Added a unified adapter (client-adapter.ts) with a new container client (client-container.ts) that routes between native JSON-RPC and container REST modes. Includes auto-detection, channels.signal.apiMode config, WebSocket event streaming, and base64 attachment encoding for the container /v2/send endpoint.
  • What did NOT change (scope boundary): Native signal-cli mode is unaffected. Inbound attachment handling is unchanged.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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 #10240

User-visible / Behavior Changes

  • Signal now works with bbernhard/signal-cli-rest-api Docker containers out of the box.
  • New channels.signal.apiMode config option ("auto" | "native" | "container"). Default "auto" auto-detects the mode.
  • Container mode uses REST (/v2/send) + WebSocket (/v1/receive/{account}) instead of JSON-RPC + SSE.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? Yes — container mode uses different endpoints (/v2/send, /v1/receive, /v1/about, /v1/typing-indicator, /v1/receipts, /v1/reactions) on the same configured httpUrl
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • Risk + mitigation: Same trust boundary as native mode — all calls go to the user-configured httpUrl. No new external network access.

Repro + Verification

Environment

  • OS: Linux
  • Runtime/container: Node 22 + bbernhard/signal-cli-rest-api container
  • Integration/channel: Signal (container mode)

Steps

  1. Run bbernhard/signal-cli-rest-api container
  2. Configure channels.signal.httpUrl pointing to the container
  3. Start gateway — auto-detection picks container mode
  4. Send/receive messages via Signal

Expected

  • Messages flow bidirectionally via WebSocket + REST

Actual (before)

  • 404 errors on /api/v1/events and /api/v1/rpc, reconnect loop

Evidence

  • Failing test/log before + passing after
  • 186 Signal tests pass (13 test files), including adapter, container, and native client tests
  • pnpm build && pnpm check && pnpm test all pass

Human Verification (required)

  • Verified scenarios: pnpm build, pnpm tsgo (0 errors), pnpm check (0 errors), pnpm test src/signal (186 tests pass)
  • Edge cases checked: username-based targets, group ID formatting, cache TTL expiry, MIME detection fallback, empty attachments
  • What you did not verify: Live containerized deployment end-to-end

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? Yes — new optional channels.signal.apiMode field (default "auto", no action needed)
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Set channels.signal.apiMode: "native" to force legacy mode
  • Files/config to restore: src/signal/client-container.ts, src/signal/client-adapter.ts
  • Known bad symptoms: Signal messages not delivered when using container mode

Risks and Mitigations

  • Risk: Auto-detection probes wrong mode if both native and container endpoints are reachable
    • Mitigation: 30s TTL cache; users can force mode via apiMode: "native" or "container"
  • Risk: Large attachments cause memory pressure during base64 encoding
    • Mitigation: Same behavior as other channels; typical attachments are small

Changed files

  • changelog/fragments/pr-signal-container-mode.md (added, +1/-0)
  • docs/channels/signal.md (modified, +56/-4)
  • extensions/signal/package.json (modified, +3/-0)
  • extensions/signal/src/client-adapter.test.ts (added, +433/-0)
  • extensions/signal/src/client-adapter.ts (added, +187/-0)
  • extensions/signal/src/client-container.test.ts (added, +638/-0)
  • extensions/signal/src/client-container.ts (added, +546/-0)
  • extensions/signal/src/core.test.ts (modified, +1/-1)
  • extensions/signal/src/monitor.tool-result.test-harness.ts (modified, +8/-2)
  • extensions/signal/src/monitor.ts (modified, +8/-1)
  • extensions/signal/src/probe.ts (modified, +1/-1)
  • extensions/signal/src/send-reactions.test.ts (modified, +1/-1)
  • extensions/signal/src/send-reactions.ts (modified, +1/-1)
  • extensions/signal/src/send.ts (modified, +1/-1)
  • extensions/signal/src/sse-reconnect.ts (modified, +9/-5)
  • pnpm-lock.yaml (modified, +4/-0)
  • src/config/types.signal.ts (modified, +9/-0)

Code Example

{
  "channels": {
    "signal": {
      "enabled": true,
      "httpUrl": "http://127.0.0.1:8080",
      "autoStart": false,
      "account": "+41XXXXXXXXX"
    }
  }
}

---

OpenClaw internal logs:
error | gateway/channels/signal | Signal SSE stream error: Error: Signal SSE failed (404 Not Found)
info  | gateway/channels/signal | Signal SSE connection lost, reconnecting in 10s...

Docker (signal-cli-rest-api) logs:
[GIN] 2026/04/28 - 11:58:24 | 404 | 603ns | 172.17.0.1 | GET "/api/v1/events?account=%2B41266732337"
[GIN] 2026/04/28 - 11:58:27 | 404 | 1.039µs | 172.17.0.1 | GET "/api/v1/check"
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

The Signal channel gateway prepends a hardcoded /api prefix to the httpUrl when connecting to an external daemon, causing 404 errors with standard signal-cli-rest-api implementations.

Steps to reproduce

  1. Deploy bbernhard/signal-cli-rest-api via Docker on port 8080.
  2. Configure OpenClaw signal channel with httpUrl: "http://127.0.0.1:8080" and autoStart: false.
  3. Start the OpenClaw gateway service.
  4. Monitor OpenClaw logs and Docker container logs.

Expected behavior

OpenClaw should connect to the Signal REST API using the base path provided or the standard /v1/... endpoints (e.g., http://127.0.0.1:8080/v1/events).

Actual behavior

OpenClaw attempts to reach endpoints with an injected /api prefix, resulting in a 404 error from the Signal daemon.

OpenClaw Log: "Signal SSE failed (404 Not Found)"

Signal-API (Docker) Log: "GET /api/v1/events?account=%2B41XXXXXXXXX" -> 404 Not Found

OpenClaw version

OpenClaw 2026.4.26 (be8c246)

Operating system

Linux pop-os 6.18.7

Install method

npm global

Model

N/A (Gateway/Channel Bug)

Provider / routing chain

OpenClaw Gateway -> Signal Channel (External Daemon) -> Docker signal-cli-rest-api

Additional provider/model setup details

{
  "channels": {
    "signal": {
      "enabled": true,
      "httpUrl": "http://127.0.0.1:8080",
      "autoStart": false,
      "account": "+41XXXXXXXXX"
    }
  }
}

Logs, screenshots, and evidence

OpenClaw internal logs:
error | gateway/channels/signal | Signal SSE stream error: Error: Signal SSE failed (404 Not Found)
info  | gateway/channels/signal | Signal SSE connection lost, reconnecting in 10s...

Docker (signal-cli-rest-api) logs:
[GIN] 2026/04/28 - 11:58:24 | 404 | 603ns | 172.17.0.1 | GET "/api/v1/events?account=%2B41266732337"
[GIN] 2026/04/28 - 11:58:27 | 404 | 1.039µs | 172.17.0.1 | GET "/api/v1/check"

Impact and severity

  • Affected users: All users using an external Signal daemon via httpUrl.
  • Severity: Blocks workflow (Channel unusable).
  • Frequency: Always.
  • Consequence: Signal messages cannot be received or sent because the SSE stream fails to initialize.

Additional information

The issue seems to be a hardcoded assumption that the REST API is served under /api. Standard signal-cli-rest-api (the most common implementation) serves directly under the root /v1. A temporary workaround requires a reverse proxy to strip the /api prefix.

extent analysis

TL;DR

Remove the hardcoded /api prefix from the Signal channel gateway's httpUrl or use a reverse proxy to strip the prefix.

Guidance

  • Verify the httpUrl configuration in the OpenClaw settings to ensure it matches the expected format without the /api prefix.
  • Check the Signal REST API documentation to confirm the standard endpoint paths, such as /v1/events.
  • Consider using a reverse proxy, like NGINX, to strip the /api prefix from incoming requests to the Signal daemon.
  • Review the OpenClaw gateway code to identify where the hardcoded /api prefix is being added and potentially remove or modify it.

Example

No code snippet is provided as the issue is related to configuration and potential code modifications, which require more context.

Notes

The provided workaround using a reverse proxy may have performance implications and should be thoroughly tested before implementation.

Recommendation

Apply a workaround, such as using a reverse proxy to strip the /api prefix, as the root cause appears to be a hardcoded assumption in the OpenClaw gateway.

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

OpenClaw should connect to the Signal REST API using the base path provided or the standard /v1/... endpoints (e.g., http://127.0.0.1:8080/v1/events).

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 - ✅(Solved) Fix Hardcoded `/api` prefix in Signal `httpUrl` causes 404 with signal-cli-rest-api [1 pull requests, 1 comments, 2 participants]