April 25, 2026 โ€ข Version: 2026.2.26

Feishu Channel WebSocket Mode: client.on is not a function

The Feishu channel crashes on startup with 'client.on is not a function' due to WebSocket client initialization failure in OpenClaw 2026.2.26.


๐Ÿ” Symptoms

Primary Error Manifestation

The Feishu channel terminates immediately upon initialization with a TypeError:

[default] channel exited: client.on is not a function

Execution Context

The error occurs during the WebSocket handshake phase, as evidenced by the log sequence:

{"subsystem":"gateway/channels/feishu","1":"starting feishu[default] (mode: websocket)"}
{"subsystem":"gateway/channels/feishu","1":"feishu[${accountId}]: bot open_id resolved: ${botOpenId ?? \"unknown\"}"}
{"subsystem":"gateway/channels/feishu","1":"[default] channel exited: client.on is not a function"}
{"subsystem":"gateway/channels/feishu","1":"[default] auto-restart attempt 3/10 in 20s"}

Diagnostic Observations

  • Bot identity resolution succeeds: The `open_id` log entry confirms API authentication (appId/appSecret) is functional.
  • Outbound communication unaffected: Sending messages via the Feishu channel works correctly.
  • Inbound processing broken: No incoming messages from Feishu users are processed.
  • Retry loop initiated: The channel enters a continuous restart cycle with exponential backoff.

CLI Inspection Commands

To manually diagnose this condition:

# Check OpenClaw version
openclaw --version
# Expected: OpenClaw v2026.2.26

# Verify Feishu channel configuration
openclaw config get channels.feishu

# View real-time logs (if logging level permits)
openclaw logs --follow --filter feishu

๐Ÿง  Root Cause

Architectural Failure Point

The error client.on is not a function originates from an incorrect WebSocket client initialization in the Feishu channel adapter. The code attempts to attach event listeners to a client object that lacks the expected EventEmitter-compatible API.

Failure Sequence Analysis

  1. Channel Initialization: OpenClaw instantiates the Feishu channel adapter in WebSocket mode.
  2. Client Creation: The adapter calls the WebSocket library constructor.
  3. API Mismatch: The code expects a standard WebSocket object with `.on(event, handler)` methods, but receives either:
    • A raw HTTP client response object
    • An improperly wrapped WebSocket connection
    • A WebSocketShim instance with incompatible API surface
  4. Event Binding Failure: The call to `client.on('message', handler)` throws TypeError: client.on is not a function.
  5. Graceful Termination: The channel catches the error and exits with the reported message.

Probable Code Path

The problematic initialization likely follows this pattern:

// Hypothetical incorrect implementation (simplified)
const WebSocket = require('ws');

class FeishuChannel {
  async connect(config) {
    const wsUrl = await this.obtainWebSocketEndpoint(config);
    
    // BUG: Using WebSocket constructor directly instead of proper connection
    const client = new WebSocket(wsUrl); // Returns WRONG object type
    
    // This fails because 'client' is not an EventEmitter
    client.on('message', (data) => this.handleMessage(data));
    client.on('error', (err) => this.handleError(err));
    
    return client;
  }
}

Dependency Context

The ws library (or equivalent) should return an object with these methods:

  • on(event: string, listener: Function): this
  • once(event: string, listener: Function): this
  • off(event: string, listener: Function): this
  • send(data: string | ArrayBuffer): void

If the returned object lacks .on(), the channel adapter was likely modified between versions, or a dependency version mismatch altered the return type.

Version Correlation

The regression appeared in OpenClaw 2026.2.26, indicating a recent change to either:

  • The Feishu channel adapter code
  • A WebSocket library dependency version
  • The build/bundling process affecting module resolution

๐Ÿ› ๏ธ Step-by-Step Fix

Method 1: Downgrade OpenClaw (Immediate Relief)

If immediate production stability is required, downgrade to a known-working version:

# Stop OpenClaw service
sudo systemctl stop openclaw  # Linux
# or
launchctl unload ~/Library/LaunchAgents/com.openclaw.plist  # macOS

# Install previous stable version
npm install -g [email protected]

# Restart service
sudo systemctl start openclaw
# or
launchctl load ~/Library/LaunchAgents/com.openclaw.plist

The WebSocket mode is unstable; switch to HTTP long-polling as an interim workaround:

{
  "channels": {
    "feishu": {
      "enabled": true,
      "dmPolicy": "pairing",
      "mode": "polling",
      "accounts": {
        "default": {
          "appId": "cli_a90c15147a38dcb3",
          "appSecret": "***",
          "botName": "Claw",
          "pollingIntervalMs": 30000
        }
      }
    }
  }
}

Note: If the configuration field mode is not recognized, proceed to Method 3.

Method 3: Apply Manual Patch (Temporary Fix)

Identify and patch the Feishu channel adapter source file:

# Locate the Feishu channel adapter
find /usr/local/lib/node_modules/openclaw -name "feishu*.js" 2>/dev/null
# or
find ~/.openclaw -name "feishu*.js" 2>/dev/null

# Common paths:
# /usr/local/lib/node_modules/openclaw/dist/gateway/channels/feishu/index.js
# ~/.openclaw/plugins/feishu/dist/index.js

Apply the following patch to the WebSocket initialization section:

// BEFORE (buggy):
const ws = new WebSocket(url);
ws.on('message', handler);

// AFTER (patched):
const WebSocket = require('ws');
const ws = new WebSocket(url);

// Verify the client has the expected API, wrap if necessary
if (typeof ws.on !== 'function') {
  // Fallback: use EventEmitter-style wrapper
  const { EventEmitter } = require('events');
  const client = new EventEmitter();
  
  ws.onmessage = (event) => client.emit('message', event.data);
  ws.onerror = (event) => client.emit('error', event);
  ws.onopen = (event) => client.emit('open', event);
  ws.onclose = (event) => client.emit('close', event);
  
  // Replace ws with wrapped client
  Object.assign(ws, client);
}

Method 4: Force WebSocket Library Override (Advanced)

If a dependency version conflict is suspected, force-load the correct WebSocket implementation:

# Check current ws dependency version
cd /usr/local/lib/node_modules/openclaw
npm ls ws

# Install specific compatible version
npm install [email protected] --save

Add to OpenClaw configuration (~/.openclaw/config.json):

{
  "feishu": {
    "websocketOptions": {
      "resolveSocket": true,
      "library": "ws"
    }
  }
}

๐Ÿงช Verification

Confirm Fix Success

After applying any fix method, verify the Feishu channel connects successfully:

Step 1: Restart OpenClaw Service

# Linux
sudo systemctl restart openclaw

# macOS
launchctl unload ~/Library/LaunchAgents/com.openclaw.plist
launchctl load ~/Library/LaunchAgents/com.openclaw.plist

# Or via PM2 (if using process manager)
pm2 restart openclaw

Step 2: Check Channel Status

openclaw status --channel feishu

# Expected output:
# feishu[default]: connected
# feishu[default]: mode: websocket  # or "polling" if using Method 2
# feishu[default]: uptime: 0d 0h 1m

Step 3: Verify WebSocket Connection (WebSocket Mode Only)

# Check for active WebSocket connection on port 9000 (default)
lsof -i :9000 | grep openclaw
# or
netstat -tlnp | grep 9000

# Expected: LISTEN or ESTABLISHED state

Step 4: Test Inbound Message Reception

Send a test message from a Feishu user to the bot:

# Monitor logs for incoming message
openclaw logs --follow --filter "feishu.*incoming"

# Send a message from Feishu app to the bot
# Expected log entry:
# {"subsystem":"gateway/channels/feishu","1":"feishu[default]: incoming message from user ${userId}"}
# {"subsystem":"gateway/channels/feishu","1":"feishu[default]: message processed successfully"}

Step 5: Verify No Restart Loop

# Check process uptime
pm2 list
# or
ps aux | grep openclaw

# Expected: stable uptime without auto-restart entries in logs

Expected Successful Log Sequence

{"subsystem":"gateway/channels/feishu","1":"starting feishu[default] (mode: websocket)"}
{"subsystem":"gateway/channels/feishu","1":"feishu[${accountId}]: bot open_id resolved: ${botOpenId}"}
{"subsystem":"gateway/channels/feishu","1":"feishu[default]: WebSocket connected"}
{"subsystem":"gateway/channels/feishu","1":"feishu[default]: channel ready"}

Exit Code Verification

# Check that the process is stable (no rapid exit)
echo $?
# Expected: 0 (process running)
# If channel is working, no 'exited' messages should appear

โš ๏ธ Common Pitfalls

  • Configuration Caching: OpenClaw may cache channel configurations. Always restart the service after config changes, and delete `~/.openclaw/cache/` if issues persist.
  • Polling Mode Not Recognized: The `mode: "polling"` configuration field may not exist in v2026.2.26. Check the channel schema with openclaw config schema channels.feishu before assuming it will work.
  • WebSocket Library Version Conflicts: If OpenClaw is installed globally alongside project-local dependencies, npm may resolve different `ws` versions. Audit with npm ls ws in both contexts.
  • Node.js Version Incompatibility: Node v25.6.0 is a very recent release. Some native WebSocket addons may not be prebuilt for this version. Consider using Node v22.x LTS as a workaround.
  • macOS ARM64 Build Artifacts: On Apple Silicon, cached native modules from x86_64 builds can cause silent failures. Run npm rebuild after any WebSocket library changes.
  • Docker Environment Isolation: If running OpenClaw in Docker, ensure the WebSocket library is installed inside the container, not mounted from the host. Volume mounts can override node_modules with incompatible builds.
  • Feishu API Rate Limiting: During the restart loop, multiple reconnection attempts may trigger Feishu's API rate limits, causing secondary authentication failures. Limit restart attempts or implement backoff.
  • Credential Rotation: If the appSecret was recently rotated on the Feishu Open Platform, the cached credentials in configuration may be stale. Re-enter credentials in the config file.
  • Channel Priority Conflicts: If multiple Feishu accounts are defined, the channel may attempt to initialize the wrong account first. Explicitly set "defaultAccount": "default" at the channel level.

Contextual Error References

  • EADDRINUSE 9000: WebSocket port conflict. Indicates another process is using the Feishu WebSocket gateway port. Check with lsof -i :9000.
  • WebSocket connection failed: 401 Unauthorized: The Feishu app credentials (appId/appSecret) are invalid or expired. Verify credentials on the Feishu Open Platform.
  • Feishu API error: app_access_token invalid: Token refresh failure. The channel cannot obtain a valid access token. Check system time synchronization.
  • channel exited: UnhandledPromiseRejection: Async initialization failure in the channel adapter. May be a precursor to the client.on error if promises are not properly handled.
  • auto-restart loop detected: OpenClaw's built-in protection against rapid restart cycles. Appears after 10 failed attempts in 2026.2.26. Use openclaw debug to bypass.
  • Cannot find module 'ws': Missing WebSocket dependency. Occurs if npm install was run with --omit=optional. Reinstall with npm install.
  • ECONNREFUSED: The Feishu WebSocket gateway is unreachable. Check firewall rules and corporate proxy settings.
  • Historical: feishu channel not starting after v2025.x update: Previously documented regression with different error message. Indicates ongoing stability issues with the Feishu channel WebSocket implementation.

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.