April 21, 2026 • 版本: 2026.2.26

Discord REST API在代理环境下失败但WebSocket正常 - Discord REST API Fails with Proxy Despite WebSocket Success

`channels.discord.proxy` 配置仅适用于 WebSocket 网关连接,不适用于出站 REST API 调用。这导致在受限区域发送消息时出现获取失败。

🔍 症状

主要症状

Discord 机器人可以成功接收消息,但无法发送消息,并抛出 TypeError: fetch failed 错误。

技术表现

日志输出模式: log [2026-02-26 10:23:45] INFO: Discord gateway connected via proxy ws://127.0.0.1:7890/ [2026-02-26 10:23:52] INFO: Received message from user123 [2026-02-26 10:23:52] ERROR: discord final reply failed: TypeError: fetch failed [2026-02-26 10:23:52] ERROR: at fetch (node:internal/deps:node_fetch:index.js:…) at async RequestClient.request (…)

CLI 诊断命令

bash

Verify Discord connection status

openclaw debug channels

Test proxy connectivity

curl -x http://127.0.0.1:7890/ https://discord.com/api/v10/gateway

Check if REST calls bypass proxy

node -e " const fetch = require(’node-fetch’); fetch(‘https://discord.com/api/v10/gateway', { agent: new (require(‘http-proxy-agent’))(‘http://127.0.0.1:7890’) }).then(r => console.log(‘Proxy works:’, r.status)).catch(e => console.error(‘Failed:’, e.message)); "

配置看起来正确但实际失败

yaml channels: discord: enabled: true token: “Bot xxx…” proxy: “http://127.0.0.1:7890/” # WebSocket uses this # No REST proxy override available

🧠 根因分析

架构问题:双重网络路径

OpenClaw 的 Discord 频道实现使用了两种不同的网络路径

操作类型网络路径代理支持
Gateway 连接 (WebSocket)discord.io WebSocket 客户端✅ 支持 channels.discord.proxy
REST API 调用(发送消息、上传附件等)carbon-request RequestClient❌ 不支持代理

代码流程分析

用户发送消息到 Discord ↓ OpenClaw 通过 WebSocket 接收(✓ 已代理) ↓ 机器人处理并需要回复 ↓ 调用 RequestClient.sendMessage() ↓ fetch() 执行时未使用代理 ↓ TypeError: fetch failed(连接被拒绝/超时)

涉及的具体文件

根本原因在于 Discord 频道处理器中调用 carbon-request / RequestClient 的方式:

javascript // In discord-channel.js (simplified) async function sendReply(message) { const client = new RequestClient(); // No agent passed return client.post({ url: https://discord.com/api/v10/channels/${channelId}/messages, body: { content: message } }); // Direct connection, no proxy }

channels.discord.proxy 设置仅被 WebSocket 初始化代码路径使用,REST 请求处理器从未使用它。

为什么接收可以正常工作

WebSocket 帧处理正确传递了代理代理: javascript const ws = new WebSocket(url, { agent: new HttpsProxyAgent(proxyUrl) // ✅ Proxy applied });

为什么发送会失败

REST 调用创建了直接连接: javascript // carbon-request internal (simplified) function request(options) { return fetch(options.url, { // No agent configuration for proxy }); }

🛠️ 逐步修复

方法 1:环境变量覆盖(临时解决方案 — 立即生效)

在启动 OpenClaw 之前设置全局代理环境变量:

bash

Linux/macOS

export HTTP_PROXY=“http://127.0.0.1:7890/” export HTTPS_PROXY=“http://127.0.0.1:7890/” export http_proxy=“http://127.0.0.1:7890/” export https_proxy=“http://127.0.0.1:7890/”

Then start OpenClaw

openclaw start

修复前后对比:

设置修复前修复后
channels.discord.proxy仅 WebSocket仅 WebSocket
环境变量未设置所有流量走代理

方法 2:代码级别修复(永久方案 — 需要补丁)

创建一个为所有 HTTP 请求应用代理的包装模块:

步骤 1: 安装所需依赖 bash npm install https-proxy-agent –save

步骤 2: 创建支持代理的 RequestClient 包装器 javascript // File: ~/.openclaw/plugins/proxy-request-wrapper.js

const { RequestClient } = require(‘carbon-request’); const { HttpsProxyAgent } = require(‘https-proxy-agent’);

class ProxyRequestClient extends RequestClient { constructor(proxyUrl) { super(); this.proxyUrl = proxyUrl; this.agent = new HttpsProxyAgent(proxyUrl); }

request(options) { return super.request({ …options, agent: this.agent, // Force HTTPS for Discord API protocol: ‘https:’ }); }

get(options) { return super.get({ …options, agent: this.agent }); }

post(options) { return super.post({ …options, agent: this.agent }); } }

module.exports = { ProxyRequestClient };

步骤 3: 修补 Discord 频道以使用包装器 javascript // In discord-channel.js, modify initialization: // Find: const client = new RequestClient(); // Replace with: const proxyUrl = config.get(‘channels.discord.proxy’) || process.env.HTTPS_PROXY; const client = proxyUrl ? new ProxyRequestClient(proxyUrl) : new RequestClient();

方法 3:用支持代理的客户端替换 carbon-request

步骤 1: 安装支持代理的 node-fetch bash npm install node-fetch@2 https-proxy-agent@5 –save

步骤 2: 在 Discord 频道中替换 RequestClient 的使用 javascript // Replace all RequestClient imports with: const fetch = require(’node-fetch’); const { HttpsProxyAgent } = require(‘https-proxy-agent’);

// In sendMessage function: async function sendMessage(channelId, content) { const proxyUrl = process.env.HTTPS_PROXY || config.channels?.discord?.proxy;

const options = { method: ‘POST’, headers: { ‘Authorization’: Bot ${config.channels.discord.token}, ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ content }) };

if (proxyUrl) { options.agent = new HttpsProxyAgent(proxyUrl); }

const response = await fetch( https://discord.com/api/v10/channels/${channelId}/messages, options );

return response.json(); }

🧪 验证

测试 1:验证 WebSocket 代理(应该已经正常工作)

bash openclaw debug –channel discord –verbose 2>&1 | grep -i proxy

预期输出: log [INFO] Discord gateway connecting via proxy: http://127.0.0.1:7890/ [INFO] Discord gateway connected (WebSocket)

测试 2:验证 REST 代理(实际修复)

javascript // Save as test-discord-rest.js const { HttpsProxyAgent } = require(‘https-proxy-agent’);

async function testProxyRequest() { const proxyUrl = ‘http://127.0.0.1:7890’;

// Test without proxy (should fail in restricted region) try { await fetch(‘https://discord.com/api/v10/gateway'); console.log(’✗ Direct connection succeeded (unexpected)’); } catch (e) { console.log(’✓ Direct connection blocked as expected:’, e.message); }

// Test with proxy (should succeed) try { const response = await fetch(‘https://discord.com/api/v10/gateway', { agent: new HttpsProxyAgent(proxyUrl) }); console.log(’✓ Proxy connection succeeded, status:’, response.status); } catch (e) { console.log(’✗ Proxy connection failed:’, e.message); } }

testProxyRequest();

运行命令: bash node test-discord-rest.js

测试 3:端到端 Discord 消息发送

bash

Start OpenClaw with proxy

HTTP_PROXY=“http://127.0.0.1:7890” HTTPS_PROXY=“http://127.0.0.1:7890” openclaw start

In another terminal, send test message via Discord

Send “!test” to your bot

Check logs

tail -f ~/.openclaw/logs/openclaw.log | grep -E “(discord|message|proxy|error)”

预期成功模式: log [INFO] Discord gateway connected via proxy [INFO] Received: !test [INFO] Sending reply via REST (proxied) [INFO] Reply sent successfully (200 OK)

测试 4:验证不再出现 fetch failed 错误

bash

After fix, this should not appear

grep -r “fetch failed” ~/.openclaw/logs/

预期结果: 无匹配(空结果)

⚠️ 常见陷阱

陷阱 1:混合使用 HTTP/HTTPS 代理协议

Discord API 要求使用 HTTPS。确保您的代理配置使用 HTTPS:

❌ 错误✅ 正确
Gateway 使用 http://127.0.0.1:7890/两者都使用(node 会处理升级)
SOCKS 代理未配置 agent明确安装 socks-proxy-agent

javascript // For SOCKS proxies: const { SocksProxyAgent } = require(‘socks-proxy-agent’); const agent = new SocksProxyAgent(‘socks://127.0.0.1:1080’);

陷阱 2:环境变量作用域问题

进程启动后设置的环境变量不会生效。始终在启动前设置:

bash

✗ Wrong - vars not inherited

openclaw start && export HTTP_PROXY="…"

✓ Correct - vars set before fork

HTTP_PROXY="…" openclaw start

陷阱 3:Node.js 版本兼容性

https-proxy-agent@5+ 需要 Node.js 18+。对于旧版本:

bash node –version

If < 18, use: npm install https-proxy-agent@4

陷阱 4:代理身份验证未处理

如果您的代理需要用户名/密码:

javascript const proxyUrl = ‘http://user:[email protected]:7890/’; // or const proxyUrl = ‘http://’ + encodeURIComponent(‘user’) + ‘:’ + encodeURIComponent(‘pass’) + ‘@127.0.0.1:7890/’;

陷阱 5:TLS 证书错误

在某些代理配置中,您可能需要禁用 SSL 验证以进行测试:

javascript process.env.NODE_TLS_REJECT_UNAUTHORIZED = ‘0’;

⚠️ 警告: 仅用于调试,生产环境中切勿使用。

陷阱 6:Docker 容器代理隔离

如果在 Docker 中运行 OpenClaw,代理必须从容器内部可访问:

bash

✗ Container can’t reach host loopback

HTTP_PROXY=“http://127.0.0.1:7890”

✓ Use host network or docker.for.mac.localhost

HTTP_PROXY=“http://host.docker.internal:7890

对于 Docker,添加 --network=host 或在 docker-compose 中配置 network_mode: host

🔗 相关错误

逻辑上相关的错误模式

错误描述相关性
TypeError: fetch failedREST 调用连接失败此问题
ECONNREFUSED代理服务器未运行网络/代理可用性
ETIMEDOUT连接到 Discord 超时网络/防火墙阻止
ENOTFOUND无法解析 Discord DNS受限区域的 DNS 过滤
Failed to connect to Discord gatewayWebSocket 初始化失败代理未应用到 WS 客户端
Disconnected with code 1006WebSocket 异常关闭网络不稳定/代理断开
Request timeoutREST 请求挂起代理慢/不稳定

历史背景

  • Issue #1423:为 Discord 网关添加了 WebSocket 代理支持
  • Issue #1891:媒体/附件下载绕过代理
  • Issue #2156:carbon-request 客户端缺少 agent 注入 API

其他频道中的类似模式

频道存在 REST 代理问题解决方案
Discord✅ 是(此问题)环境变量 / 代码补丁
Slack⚠️ 部分使用带 agent 的 node-fetch
Teams❌ 未知需要测试
Mattermost❌ 未知需要测试

依据与来源

本故障排除指南由 FixClaw 智能管线从社区讨论中自动合成。