Discord REST API Fails with Proxy Despite WebSocket Success
The `channels.discord.proxy` configuration only applies to WebSocket gateway connections, not to outbound REST API calls. This causes fetch failures when sending messages in restricted regions.
๐ Symptoms
Primary Symptom
Discord bot receives messages successfully but fails to send them, throwing a TypeError: fetch failed error.
Technical Manifestations
Log Output Pattern: 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 Diagnostic Commands
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)); "
Configuration that Appears Correct but Fails
yaml channels: discord: enabled: true token: “Bot xxx…” proxy: “http://127.0.0.1:7890/” # WebSocket uses this # No REST proxy override available
๐ง Root Cause
Architectural Issue: Dual Network Paths
OpenClaw’s Discord channel implementation uses two distinct network paths:
| Operation Type | Network Path | Proxy Support |
|---|---|---|
| Gateway Connection (WebSocket) | discord.io WebSocket client | โ
Respects channels.discord.proxy |
| REST API Calls (send message, upload attachment, etc.) | carbon-request RequestClient | โ No proxy support |
Code Flow Analysis
User sends message to Discord โ OpenClaw receives via WebSocket (โ proxied) โ Bot processes and needs to reply โ RequestClient.sendMessage() called โ fetch() executes WITHOUT proxy agent โ TypeError: fetch failed (connection refused/timeout)
Specific File Involvement
The root cause resides in how carbon-request / RequestClient is invoked in the Discord channel handler:
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
}
The channels.discord.proxy setting is only consumed by the WebSocket initialization code path, never by the REST request handler.
Why It Works for Receiving
The WebSocket frame handling correctly passes the proxy agent: javascript const ws = new WebSocket(url, { agent: new HttpsProxyAgent(proxyUrl) // โ Proxy applied });
Why It Fails for Sending
The REST call creates a direct connection: javascript // carbon-request internal (simplified) function request(options) { return fetch(options.url, { // No agent configuration for proxy }); }
๐ ๏ธ Step-by-Step Fix
Option 1: Environment Variable Override (Workaround - Immediate)
Set global proxy environment variables before launching 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
Before vs After:
| Setting | Before | After |
|---|---|---|
channels.discord.proxy | WebSocket only | WebSocket only |
| Environment variables | Not set | All traffic proxied |
Option 2: Code-level Fix (Permanent - Requires Patching)
Create a wrapper module that applies proxy to all HTTP requests:
Step 1: Install required dependency bash npm install https-proxy-agent –save
Step 2: Create proxy-aware RequestClient wrapper 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 };
Step 3: Patch Discord channel to use wrapper 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();
Option 3: Replace carbon-request with proxy-aware client
Step 1: Install node-fetch with agent support bash npm install node-fetch@2 https-proxy-agent@5 –save
Step 2: Replace RequestClient usage in Discord channel 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(); }
๐งช Verification
Test 1: Verify WebSocket Proxy (Should Already Work)
bash openclaw debug –channel discord –verbose 2>&1 | grep -i proxy
Expected Output: log [INFO] Discord gateway connecting via proxy: http://127.0.0.1:7890/ [INFO] Discord gateway connected (WebSocket)
Test 2: Verify REST Proxy (The Actual Fix)
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();
Run with: bash node test-discord-rest.js
Test 3: End-to-End Discord Message Send
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)”
Expected Success Pattern: log [INFO] Discord gateway connected via proxy [INFO] Received: !test [INFO] Sending reply via REST (proxied) [INFO] Reply sent successfully (200 OK)
Test 4: Verify No fetch failed Error
bash
After fix, this should not appear
grep -r “fetch failed” ~/.openclaw/logs/
Expected: No matches (empty result)
โ ๏ธ Common Pitfalls
Pitfall 1: Mixing HTTP/HTTPS Proxy Protocols
Discord API requires HTTPS. Ensure your proxy configuration uses HTTPS:
| โ Wrong | โ Correct |
|---|---|
http://127.0.0.1:7890/ for gateway | Use for both (node handles upgrade) |
| SOCKS proxy without agent config | Install socks-proxy-agent explicitly |
javascript // For SOCKS proxies: const { SocksProxyAgent } = require(‘socks-proxy-agent’); const agent = new SocksProxyAgent(‘socks://127.0.0.1:1080’);
Pitfall 2: Environment Variable Scoping
Environment variables set after process start don’t apply. Always set before launching:
bash
โ Wrong - vars not inherited
openclaw start && export HTTP_PROXY="…"
โ Correct - vars set before fork
HTTP_PROXY="…" openclaw start
Pitfall 3: Node.js Version Compatibility
https-proxy-agent@5+ requires Node.js 18+. For older versions:
bash node –version
If < 18, use: npm install https-proxy-agent@4
Pitfall 4: Proxy Authentication Not Handled
If your proxy requires username/password:
javascript const proxyUrl = ‘http://user:[email protected]:7890/’; // or const proxyUrl = ‘http://’ + encodeURIComponent(‘user’) + ‘:’ + encodeURIComponent(‘pass’) + ‘@127.0.0.1:7890/’;
Pitfall 5: TLS Certificate Errors
In some proxy configurations, you may need to disable SSL verification for testing:
javascript process.env.NODE_TLS_REJECT_UNAUTHORIZED = ‘0’;
โ ๏ธ Warning: Only use this for debugging. Never in production.
Pitfall 6: Docker Container Proxy Isolation
If running OpenClaw in Docker, proxy must be accessible from within the container:
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”
For Docker, add --network=host or configure network_mode: host in docker-compose.
๐ Related Errors
Logically Connected Error Patterns
| Error | Description | Related To |
|---|---|---|
TypeError: fetch failed | REST call connection failure | This issue |
ECONNREFUSED | Proxy server not running | Network/proxy availability |
ETIMEDOUT | Connection timeout to Discord | Network/firewall blocking |
ENOTFOUND | Cannot resolve Discord DNS | DNS filtering in restricted regions |
Failed to connect to Discord gateway | WebSocket init failure | Proxy not applied to WS client |
Disconnected with code 1006 | WebSocket abnormal closure | Network instability/proxy dropping |
Request timeout | REST request hanging | Proxy slow/unstable |
Historical Context
- Issue #1423: WebSocket proxy support added for Discord gateway
- Issue #1891: Media/attachment downloads bypass proxy
- Issue #2156: carbon-request client lacks agent injection API
Similar Patterns in Other Channels
| Channel | Has REST Proxy Issue | Workaround |
|---|---|---|
| Discord | โ Yes (this issue) | Env vars / code patch |
| Slack | โ ๏ธ Partial | Uses node-fetch with agent |
| Teams | โ Unknown | Requires testing |
| Mattermost | โ Unknown | Requires testing |