Skip to content

2026.3.1: controlUi HTTP handler blocks all webhook POSTs with 405 #31983

@1kuna

Description

@1kuna

Description

After upgrading to 2026.3.1, all BlueBubbles webhook POST requests return 405 Method Not Allowed. The gateway starts fine and reports BlueBubbles as OK in openclaw status --deep, but incoming iMessages are never delivered because the webhook handler never receives them.

Root Cause

handleControlUiHttpRequest in gateway-cli-tzSO700C.js (line ~16917) rejects all non-GET/HEAD requests with 405 before checking the URL path:

function handleControlUiHttpRequest(req, res, opts) {
    const urlRaw = req.url;
    if (!urlRaw) return false;
    if (req.method !== "GET" && req.method !== "HEAD") {
        res.statusCode = 405;
        res.end("Method Not Allowed");
        return true; // <-- claims it handled the request
    }
    // ...basePath matching happens AFTER, never reached for POST
}

Since 2026.3.1 changed handler ordering to "run plugin handlers after built-in handlers for deterministic route precedence" (Gateway/Plugin HTTP auth hardening), the controlUi handler now runs before plugin webhook routes. Every POST to any path gets caught and 405'd before the BB webhook plugin handler can process it.

Reproduction

  1. Fresh install or upgrade to 2026.3.1
  2. Configure BlueBubbles channel (working config, BB server healthy)
  3. Send an iMessage — never delivered
  4. Confirm with curl:
    curl -X POST http://127.0.0.1:18789/bluebubbles-webhook?password=xxx -H 'Content-Type: application/json' -d '{}'
    # Returns: 405 Method Not Allowed

Workaround

Disable controlUi in config:

{
  "gateway": {
    "controlUi": {
      "enabled": false
    }
  }
}

Then openclaw gateway restart. Webhooks return 200 again. Downside: dashboard is inaccessible.

Suggested Fix

Move the method check after the basePath match so non-matching paths return false and fall through to plugin handlers:

function handleControlUiHttpRequest(req, res, opts) {
    const urlRaw = req.url;
    if (!urlRaw) return false;
    const url = new URL(urlRaw, "http://localhost");
    const basePath = normalizeControlUiBasePath(opts?.basePath);
    // Check path first — if this isn't a controlUi route, don't handle it
    if (basePath && url.pathname !== basePath && !url.pathname.startsWith(basePath + "/")) {
        return false;
    }
    // Now safe to enforce method
    if (req.method !== "GET" && req.method !== "HEAD") {
        res.statusCode = 405;
        res.end("Method Not Allowed");
        return true;
    }
    // ...rest of handler
}

Environment

  • OpenClaw: 2026.3.1
  • macOS: 26.3.0 (arm64)
  • Node: 25.5.0
  • BlueBubbles Server: 1.9.9
  • Gateway bind: loopback, port 18789

Affected Channels

This likely affects all webhook-based channels (BlueBubbles, Telegram webhook mode, Google Chat webhook, etc.), not just BlueBubbles.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions