[La API REST de Discord falla con proxy pese al éxito de WebSocket] - Discord REST API Fails with Proxy Despite WebSocket Success
La configuración `channels.discord.proxy` solo se aplica a las conexiones de puerta de enlace WebSocket, no a las llamadas salientes de la API REST. Esto causa fallos de recuperación al enviar mensajes en regiones restringidas.
🔍 Síntomas
Síntoma principal
El bot de Discord recibe mensajes correctamente pero falla al enviarlos, arrojando un error TypeError: fetch failed.
Manifestaciones técnicas
Patrón de salida del log: 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 (…)
Comandos de diagnóstico CLI
bash
Verificar estado de conexión de Discord
openclaw debug channels
Probar conectividad del proxy
curl -x http://127.0.0.1:7890/ https://discord.com/api/v10/gateway
Verificar si las llamadas REST omiten el 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)); "
Configuración que parece correcta pero falla
yaml channels: discord: enabled: true token: “Bot xxx…” proxy: “http://127.0.0.1:7890/” # WebSocket usa esto # No hay anulación de proxy REST disponible
🧠 Causa raíz
Problema arquitectónico: rutas de red duales
La implementación del canal Discord de OpenClaw utiliza dos rutas de red distintas:
| Tipo de operación | Ruta de red | Soporte de proxy |
|---|---|---|
| Conexión Gateway (WebSocket) | Cliente WebSocket discord.io | ✅ Respeta channels.discord.proxy |
| Llamadas API REST (enviar mensaje, subir adjunto, etc.) | RequestClient de carbon-request | ❌ Sin soporte de proxy |
Análisis del flujo de código
El usuario envía un mensaje a Discord ↓ OpenClaw recibe a través de WebSocket (✓ proxy aplicado) ↓ El bot procesa y necesita responder ↓ Se llama a RequestClient.sendMessage() ↓ fetch() se ejecuta SIN agente de proxy ↓ TypeError: fetch failed (conexión rechazada/tiempo de espera agotado)
Archivo específico involucrado
La causa raíz reside en cómo se invoca carbon-request / RequestClient en el manejador del canal Discord:
javascript
// En discord-channel.js (simplificado)
async function sendReply(message) {
const client = new RequestClient(); // Sin agente pasado
return client.post({
url: https://discord.com/api/v10/channels/${channelId}/messages,
body: { content: message }
}); // Conexión directa, sin proxy
}
La configuración channels.discord.proxy solo es consumida por la ruta de inicialización del WebSocket, nunca por el manejador de solicitudes REST.
Por qué funciona para recibir
El manejo de tramas WebSocket pasa correctamente el agente de proxy: javascript const ws = new WebSocket(url, { agent: new HttpsProxyAgent(proxyUrl) // ✅ Proxy aplicado });
Por qué falla para enviar
La llamada REST crea una conexión directa: javascript // carbon-request interno (simplificado) function request(options) { return fetch(options.url, { // Sin configuración de agente para proxy }); }
🛠️ Solución paso a paso
Opción 1: Invalidación de variables de entorno (Alternativa - Inmediata)
Establece variables de entorno de proxy globales antes de iniciar 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/”
Luego inicia OpenClaw
openclaw start
Antes vs Después:
| Configuración | Antes | Después |
|---|---|---|
channels.discord.proxy | Solo WebSocket | Solo WebSocket |
| Variables de entorno | No establecidas | Todo el tráfico con proxy |
Opción 2: Corrección a nivel de código (Permanente - Requiere parche)
Crea un módulo wrapper que aplique proxy a todas las solicitudes HTTP:
Paso 1: Instalar dependencia requerida bash npm install https-proxy-agent –save
Paso 2: Crear wrapper de RequestClient consciente de proxy javascript // Archivo: ~/.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, // Forzar HTTPS para API de Discord protocol: ‘https:’ }); }
get(options) { return super.get({ …options, agent: this.agent }); }
post(options) { return super.post({ …options, agent: this.agent }); } }
module.exports = { ProxyRequestClient };
Paso 3: Aplicar parche al canal Discord para usar el wrapper javascript // En discord-channel.js, modificar inicialización: // Encontrar: const client = new RequestClient(); // Reemplazar con: const proxyUrl = config.get(‘channels.discord.proxy’) || process.env.HTTPS_PROXY; const client = proxyUrl ? new ProxyRequestClient(proxyUrl) : new RequestClient();
Opción 3: Reemplazar carbon-request con cliente consciente de proxy
Paso 1: Instalar node-fetch con soporte de agente bash npm install node-fetch@2 https-proxy-agent@5 –save
Paso 2: Reemplazar uso de RequestClient en canal Discord javascript // Reemplazar todas las importaciones de RequestClient con: const fetch = require(’node-fetch’); const { HttpsProxyAgent } = require(‘https-proxy-agent’);
// En función sendMessage: 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(); }
🧪 Verificación
Prueba 1: Verificar proxy WebSocket (Debería funcionar)
bash openclaw debug –channel discord –verbose 2>&1 | grep -i proxy
Salida esperada: log [INFO] Discord gateway connecting via proxy: http://127.0.0.1:7890/ [INFO] Discord gateway connected (WebSocket)
Prueba 2: Verificar proxy REST (La corrección real)
javascript // Guardar como test-discord-rest.js const { HttpsProxyAgent } = require(‘https-proxy-agent’);
async function testProxyRequest() { const proxyUrl = ‘http://127.0.0.1:7890’;
// Probar sin proxy (debería fallar en región restringida) try { await fetch(‘https://discord.com/api/v10/gateway'); console.log(’✗ Conexión directa réussie (inesperado)’); } catch (e) { console.log(’✓ Conexión directa bloqueada como esperado:’, e.message); }
// Probar con proxy (debería funcionar) try { const response = await fetch(‘https://discord.com/api/v10/gateway', { agent: new HttpsProxyAgent(proxyUrl) }); console.log(’✓ Conexión por proxy réussie, estado:’, response.status); } catch (e) { console.log(’✗ Conexión por proxy falló:’, e.message); } }
testProxyRequest();
Ejecutar con: bash node test-discord-rest.js
Prueba 3: Envío de mensaje Discord de extremo a extremo
bash
Iniciar OpenClaw con proxy
HTTP_PROXY=“http://127.0.0.1:7890” HTTPS_PROXY=“http://127.0.0.1:7890” openclaw start
En otra terminal, enviar mensaje de prueba vía Discord
Enviar “!test” a tu bot
Verificar logs
tail -f ~/.openclaw/logs/openclaw.log | grep -E “(discord|message|proxy|error)”
Patrón de éxito esperado: log [INFO] Discord gateway connected via proxy [INFO] Received: !test [INFO] Sending reply via REST (proxied) [INFO] Reply sent successfully (200 OK)
Prueba 4: Verificar que no haya error fetch failed
bash
Después de la corrección, esto no debería aparecer
grep -r “fetch failed” ~/.openclaw/logs/
Esperado: Sin coincidencias (resultado vacío)
⚠️ Errores comunes
Error común 1: Mezclar protocolos de proxy HTTP/HTTPS
La API de Discord requiere HTTPS. Asegúrate de que tu configuración de proxy use HTTPS:
| ❌ Incorrecto | ✅ Correcto |
|---|---|
http://127.0.0.1:7890/ para gateway | Usar para ambos (node maneja el upgrade) |
| Proxy SOCKS sin configuración de agente | Instalar socks-proxy-agent explícitamente |
javascript // Para proxies SOCKS: const { SocksProxyAgent } = require(‘socks-proxy-agent’); const agent = new SocksProxyAgent(‘socks://127.0.0.1:1080’);
Error común 2: Alcance de variables de entorno
Las variables de entorno establecidas después del inicio del proceso no se aplican. Siempre establece antes de lanzar:
bash
✗ Incorrecto - vars no heredadas
openclaw start && export HTTP_PROXY="…"
✓ Correcto - vars establecidas antes del fork
HTTP_PROXY="…" openclaw start
Error común 3: Compatibilidad de versión de Node.js
https-proxy-agent@5+ requiere Node.js 18+. Para versiones anteriores:
bash node –version
Si < 18, usar: npm install https-proxy-agent@4
Error común 4: Autenticación de proxy no manejada
Si tu proxy requiere nombre de usuario y contraseña:
javascript const proxyUrl = ‘http://user:[email protected]:7890/’; // o const proxyUrl = ‘http://’ + encodeURIComponent(‘user’) + ‘:’ + encodeURIComponent(‘pass’) + ‘@127.0.0.1:7890/’;
Error común 5: Errores de certificado TLS
En algunas configuraciones de proxy, puede que necesites deshabilitar la verificación SSL para pruebas:
javascript process.env.NODE_TLS_REJECT_UNAUTHORIZED = ‘0’;
⚠️ Advertencia: Solo usar esto para depuración. Nunca en producción.
Error común 6: Aislamiento de proxy de contenedor Docker
Si ejecutas OpenClaw en Docker, el proxy debe ser accesible desde dentro del contenedor:
bash
✗ El contenedor no puede alcanzar el loopback del host
HTTP_PROXY=“http://127.0.0.1:7890”
✓ Usar red del host o docker.for.mac.localhost
HTTP_PROXY=“http://host.docker.internal:7890”
Para Docker, añade --network=host o configura network_mode: host en docker-compose.
🔗 Errores relacionados
Patrones de errores lógicamente conectados
| Error | Descripción | Relacionado con |
|---|---|---|
TypeError: fetch failed | Fallo de conexión de llamada REST | Este problema |
ECONNREFUSED | Servidor proxy no ejecutándose | Disponibilidad de red/proxy |
ETIMEDOUT | Tiempo de conexión agotado a Discord | Red/firewall bloqueando |
ENOTFOUND | No puede resolver DNS de Discord | Filtrado DNS en regiones restringidas |
Failed to connect to Discord gateway | Fallo de inicialización WebSocket | Proxy no aplicado a cliente WS |
Disconnected with code 1006 | Cierre anormal de WebSocket | Inestabilidad de red/proxy cayendo |
Request timeout | Solicitud REST colgada | Proxy lento/inestable |
Contexto histórico
- Issue #1423: Soporte de proxy WebSocket agregado para Discord gateway
- Issue #1891: Descargas de medios/adjuntos omiten proxy
- Issue #2156: Cliente carbon-request carece de API de inyección de agente
Patrones similares en otros canales
| Canal | Tiene problema de proxy REST | Alternativa |
|---|---|---|
| Discord | ✅ Sí (este problema) | Vars de entorno / parche de código |
| Slack | ⚠️ Parcial | Usa node-fetch con agente |
| Teams | ❌ Desconocido | Requiere pruebas |
| Mattermost | ❌ Desconocido | Requiere pruebas |