[飞书频道WebSocket模式client.on方法错误] - Feishu Channel WebSocket Mode: client.on is not a function
飞书频道在启动时崩溃,提示'client.on不是函数',原因是OpenClaw 2026.2.26中WebSocket客户端初始化失败。
🔍 症状
主要错误表现
飞书频道在初始化时立即终止,并抛出 TypeError:
[default] channel exited: client.on is not a function执行上下文
根据日志序列显示,错误发生在 WebSocket 握手阶段:
{"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"}诊断观察
- 机器人身份解析成功:`open_id` 日志条目确认 API 身份验证(appId/appSecret)正常工作。
- 出站通信不受影响:通过飞书频道发送消息正常工作。
- 入站处理中断:飞书用户发送的入站消息无法被处理。
- 重试循环已启动:频道进入持续的重启循环,采用指数退避策略。
CLI 诊断命令
要手动诊断此问题:
# 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🧠 根因分析
架构故障点
错误 client.on is not a function 源于飞书频道适配器中不正确的 WebSocket 客户端初始化。代码尝试向缺少预期 EventEmitter 兼容 API 的客户端对象附加事件监听器。
故障序列分析
- 频道初始化:OpenClaw 在 WebSocket 模式下实例化飞书频道适配器。
- 客户端创建:适配器调用 WebSocket 库构造函数。
- API 不匹配:代码期望具有 `.on(event, handler)` 方法的标准 WebSocket 对象,但实际上收到的是:
- 原始 HTTP 客户端响应对象
- 包装不当的 WebSocket 连接
- 具有不兼容 API 表面的 WebSocketShim 实例
- 事件绑定失败:对 `client.on('message', handler)` 的调用抛出
TypeError: client.on is not a function。 - 优雅终止:频道捕获错误并以报告的消息退出。
可能的代码路径
有问题的初始化可能遵循以下模式:
// 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;
}
}依赖上下文
ws 库(或等效库)应返回具有以下方法的对象:
on(event: string, listener: Function): thisonce(event: string, listener: Function): thisoff(event: string, listener: Function): thissend(data: string | ArrayBuffer): void
如果返回的对象缺少 .on(),则频道适配器可能在版本之间被修改,或者依赖版本不匹配改变了返回类型。
版本关联
此回归问题出现在 OpenClaw 2026.2.26 中,表明最近可能更改了:
- 飞书频道适配器代码
- WebSocket 库依赖版本
- 影响模块解析的构建/打包过程
🛠️ 逐步修复
方法 1:降级 OpenClaw(立即缓解)
如果需要立即恢复生产稳定性,降级到已知可用的版本:
# 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方法 2:切换到 HTTP 长轮询模式(推荐)
WebSocket 模式不稳定;切换到 HTTP 长轮询作为临时解决方案:
{
"channels": {
"feishu": {
"enabled": true,
"dmPolicy": "pairing",
"mode": "polling",
"accounts": {
"default": {
"appId": "cli_a90c15147a38dcb3",
"appSecret": "***",
"botName": "Claw",
"pollingIntervalMs": 30000
}
}
}
}
}注意:如果配置字段 mode 无法识别,请继续执行方法 3。
方法 3:应用手动补丁(临时修复)
找到并修补飞书频道适配器源文件:
# 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在 WebSocket 初始化部分应用以下补丁:
// 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);
}方法 4:强制覆盖 WebSocket 库(高级)
如果怀疑是依赖版本冲突,强制加载正确的 WebSocket 实现:
# Check current ws dependency version
cd /usr/local/lib/node_modules/openclaw
npm ls ws
# Install specific compatible version
npm install [email protected] --save添加到 OpenClaw 配置(~/.openclaw/config.json):
{
"feishu": {
"websocketOptions": {
"resolveSocket": true,
"library": "ws"
}
}
}🧪 验证
确认修复成功
应用任何修复方法后,验证飞书频道是否成功连接:
步骤 1:重启 OpenClaw 服务
# 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步骤 2:检查频道状态
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步骤 3:验证 WebSocket 连接(仅 WebSocket 模式)
# Check for active WebSocket connection on port 9000 (default)
lsof -i :9000 | grep openclaw
# or
netstat -tlnp | grep 9000
# Expected: LISTEN or ESTABLISHED state步骤 4:测试入站消息接收
从飞书用户向机器人发送测试消息:
# 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"}步骤 5:验证无重启循环
# Check process uptime
pm2 list
# or
ps aux | grep openclaw
# Expected: stable uptime without auto-restart entries in logs预期的成功日志序列
{"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"}退出码验证
# Check that the process is stable (no rapid exit)
echo $?
# Expected: 0 (process running)
# If channel is working, no 'exited' messages should appear⚠️ 常见陷阱
- 配置缓存:OpenClaw 可能会缓存频道配置。更改配置后务必重启服务,如果问题仍然存在,请删除 `~/.openclaw/cache/`。
- 轮询模式无法识别:`mode: "polling"` 配置字段可能不存在于 v2026.2.26 中。在假设其可用之前,请使用
openclaw config schema channels.feishu检查频道 schema。 - WebSocket 库版本冲突:如果 OpenClaw 与项目本地依赖一起全局安装,npm 可能会解析不同的 `ws` 版本。请在两个上下文中使用
npm ls ws进行审计。 - Node.js 版本不兼容:Node v25.6.0 是一个非常新的版本。某些原生 WebSocket 插件可能未为此版本预构建。请考虑使用 Node v22.x LTS 作为临时解决方案。
- macOS ARM64 构建产物:在 Apple Silicon 上,来自 x86_64 构建的缓存原生模块可能导致静默失败。在任何 WebSocket 库更改后运行
npm rebuild。 - Docker 环境隔离:如果在 Docker 中运行 OpenClaw,请确保 WebSocket 库安装在容器内,而不是从主机挂载。卷挂载可能会用不兼容的构建覆盖
node_modules。 - 飞书 API 速率限制:在重启循环期间,多次重连尝试可能会触发飞书的 API 速率限制,导致次级身份验证失败。限制重启尝试或实施退避策略。
- 凭证轮换:如果在飞书开放平台上最近轮换了 appSecret,配置中的缓存凭证可能已过期。请在配置文件中重新输入凭证。
- 频道优先级冲突:如果定义了多个飞书账户,频道可能会首先尝试初始化错误的账户。请在频道级别明确设置
"defaultAccount": "default"。
🔗 相关错误
上下文错误参考
EADDRINUSE 9000:WebSocket 端口冲突。表示另一个进程正在使用飞书 WebSocket 网关端口。使用lsof -i :9000检查。WebSocket connection failed: 401 Unauthorized:飞书应用凭证(appId/appSecret)无效或已过期。在飞书开放平台上验证凭证。Feishu API error: app_access_token invalid:令牌刷新失败。频道无法获取有效访问令牌。检查系统时间同步。channel exited: UnhandledPromiseRejection:频道适配器中的异步初始化失败。如果 Promise 未正确处理,这可能是client.on错误的前兆。auto-restart loop detected:OpenClaw 内置的防止快速重启循环的保护机制。在 2026.2.26 中连续 10 次失败尝试后出现。使用openclaw debug绕过。Cannot find module 'ws':缺少 WebSocket 依赖。如果npm install使用了--omit=optional运行,则会发生此错误。使用npm install重新安装。ECONNREFUSED:飞书 WebSocket 网关无法访问。检查防火墙规则和公司代理设置。Historical: feishu channel not starting after v2025.x update:之前记录的回归问题,具有不同的错误消息。表明飞书频道 WebSocket 实现存在持续稳定性问题。