La descarga de medios de Telegram falla a través del proxy HTTP después de la actualización a v2026.4.8
La regresión causada por la omisión de DNS pinning en el modo trusted env-proxy interrumpe las descargas de medios del CDN de Telegram mientras los mensajes de texto continúan funcionando con normalidad.
🔍 Síntomas
Manifestación principal del error
Las descargas de medios de Telegram fallan con un mensaje de error genérico:
Error: could not download media
at TelegramMediaDownloader.download (/app/src/channels/telegram/media.ts:142:17)
at TelegramMessageHandler.processMedia (/app/src/channels/telegram/handler.ts:89:24)
at async TelegramChannel.processUpdate (/app/src/channels/telegram/channel.ts:67:33)Operaciones afectadas
Los siguientes tipos de medios fallan a través del proxy HTTP configurado:
- Imágenes (fotos enviadas/recibidas en chats)
- Mensajes de voz (archivos de audio .ogg)
- Notas de video
- Archivos adjuntos (archivos a través de CDN)
Operaciones que continúan funcionando
- Envío/recepción de mensajes de texto
- Comandos de gestión de canales
- Procesamiento de comandos del bot
- Recuperación de stickers (archivos pequeños)
Indicadores de diagnóstico
# Log entries observed during failed media download:
[WARN] NetworkGuard: target "cdn.telegram.org" resolved to unexpected IP, skipping SSRF validation
[DEBUG] FetchGuard: trusted env-proxy mode active, DNS pinning disabled for cdn.telegram.org
[ERROR] MediaDownload: HTTP 403 Forbidden from https://cdn.telegram.org/_/files/...
# Configured proxy test:
$ curl -x http://100.80.46.108:8888 https://api.telegram.org -v
< HTTP/1.1 200 OK # Works for API calls
$ curl -x http://100.80.46.108:8888 https://cdn.telegram.org/_/files/... -v
< HTTP/1.1 403 Forbidden # Fails for CDNContexto del entorno
Host OS: macOS 14.x (arm64)
Proxy: Tinyproxy 1.11.x on remote AWS (jp-aws)
Proxy Network: Tailscale (100.80.46.108:8888)
Firewall: GFW (China mainland)
OpenClaw Config: channels.telegram.proxy = "http://100.80.46.108:8888"
Affected Versions: v2026.4.8, v2026.4.9, v2026.4.10, v2026.4.11, v2026.4.12🧠 Causa raíz
Fondo arquitectónico: Los PRs en conflicto
La regresión se origina de la interacción entre dos pull requests con objetivos opuestos:
PR #62878 (área v2026.4.7)
Objetivo: Permitir nombres de host de proxy configurados por el operador en fetchs protegidos por SSRF
Efecto: Las descargas de medios a través de proxies configurados comenzaron a funcionar correctamente
Mecanismo: Modificado FetchGuard para confiar en los nombres de host de proxy especificados en channels.telegram.proxy
PR #59007 (v2026.4.8)
Objetivo: Omitir el pinado de DNS cuando el modo de proxy env de confianza está activo
Efecto: Disruptó el mecanismo del que dependía PR #62878
Mecanismo: Cuando se detecta modo de proxy env de confianza, los controles de pinado de DNS se omiten para el objetivo del proxy
Análisis de la secuencia de fallos
1. User configures: channels.telegram.proxy = "http://100.80.46.108:8888"
2. OpenClaw initiates Telegram media download via:
TelegramMediaDownloader.download() → FetchGuard.execute()
3. FetchGuard detects:
- trusted env-proxy mode = ACTIVE (proxy URL present in config)
- target = "cdn.telegram.org" (Telegram CDN domain)
4. PR #59007 logic path executes:
if (envProxyMode && skipDNSPinning) {
// Bypass DNS pinning validation for proxy targets
proceedWithoutPinningCheck()
}
5. Missing Validation Gap:
- DNS pinning is skipped
- BUT the actual IP resolution happens through the proxy tunnel
- cdn.telegram.org resolves to different IPs through proxy vs direct
6. CDN Rejects Request:
- Telegram CDN validates request origin by Host header + IP binding
- The "skipped" DNS pinning caused a routing inconsistency
- CDN responds: HTTP 403 Forbidden
7. Result: "could not download media" error bubbles upCausa raíz a nivel de código
El problema reside en src/network/fetch-guard.ts:
// v2026.4.8+ implementation (PROBLEMATIC)
async executeFetch(url: string, options: FetchOptions): Promise {
const targetHost = new URL(url).hostname;
if (this.isEnvProxyMode() && this.isTrustedProxyTarget(targetHost)) {
// PR #59007: Skip DNS pinning when proxy handles resolution
this.logger.debug(`FetchGuard: trusted env-proxy mode active, ` +
`DNS pinning disabled for ${targetHost}`);
// BUG: Missing validation that proxy correctly resolves CDN domains
return this.directFetch(url, options); // <-- Wrong path chosen
}
// Normal path with DNS pinning (unreachable for Telegram CDN with proxy)
return this.guardedFetch(url, options);
} El bug: Cuando se detecta el modo de proxy env de confianza, el código asume que el proxy manejará la resolución de DNS correctamente pero no realiza ninguna validación. Los dominios CDN de Telegram requieren una validación específica de enlace de IP que el proxy interrumpe.
Por qué los mensajes de texto aún funcionan
Las llamadas a la API de Telegram (api.telegram.org) usan una clasificación de endpoint diferente:
- Los endpoints de API están en la lista blanca en `TRUSTED_API_HOSTS`
- Evitan el pinado de DNS completamente a través de una ruta de código separada
- Los endpoints CDN de medios (`cdn.telegram.org`, `*.t.me`) tienen validación de IP más estricta
🛠️ Solución paso a paso
Opción A: Solución basada en configuración (Inmediata)
Añadir manejo explícito de dominios CDN para evadir la regresión sin cambios en el código:
Paso 1: Localiza tu configuración openclaw.json
# Standard locations
macOS/Linux: ~/.config/openclaw/openclaw.json
Docker: /app/config/openclaw.json
Systemd service: /etc/openclaw/openclaw.jsonPaso 2: Añade dominios CDN a los hosts de confianza
{
"channels": {
"telegram": {
"proxy": "http://100.80.46.108:8888",
"trustedMediaHosts": [
"cdn.telegram.org",
"*.t.me",
"api.telegram.org"
]
}
},
"network": {
"fetchGuard": {
"dnsPinning": {
"enabled": false,
"trustedHosts": ["cdn.telegram.org", "api.telegram.org", "*.t.me"]
}
}
}
}Paso 3: Reinicia OpenClaw
# Systemd
sudo systemctl restart openclaw
# Docker
docker restart openclaw
# Direct process
pkill -f openclaw && openclawOpción B: Desactivar el pinado de DNS para descargas de medios (Dirigido)
Si la Opción A no resuelve el problema, desactiva temporalmente el pinado de DNS para medios de Telegram:
Paso 1: Crea archivo de override de entorno
cat > /etc/openclaw/overrides/telegram-media.env << 'EOF'
OPENCLAW_TELEGRAM_DNS_PINNING=disabled
OPENCLAW_FETCH_GUARD_STRICT_MODE=false
EOFPaso 2: Verifica que el entorno está cargado
# Check that OpenClaw reads the override
openclaw doctor --env | grep -i telegram
# Expected output:
# TELEGRAM_DNS_PINNING=disabled
# FETCH_GUARD_STRICT_MODE=falsePaso 3: Prueba la descarga de medios
# Send a test image to your bot and check logs
tail -f /var/log/openclaw/openclaw.log | grep -i mediaOpción C: Retroceder a la última versión conocida buena (Definitivo)
# Docker deployment
# Edit docker-compose.yml:
image: openclaw/openclaw:v2026.4.7
# Pull and restart
docker-compose pull
docker-compose up -d
# Verify version
docker exec openclaw openclaw --version
# Output: v2026.4.7Opción D: Aplicar parche al Fetch Guard (Corrección permanente pendiente)
Espera la corrección en v2026.4.13+ o aplica el parche manualmente:
Paso 1: Localiza fetch-guard.ts
find /app -name "fetch-guard.ts" -type f 2>/dev/null
# Output: /app/src/network/fetch-guard.tsPaso 2: Aplica el parche dirigido
# Create patch file: fetch-guard-fix.patch
--- a/src/network/fetch-guard.ts
+++ b/src/network/fetch-guard.ts
@@ -142,9 +142,15 @@ export class FetchGuard {
return this.guardedFetch(url, options);
}
+ // Validate proxy can resolve target before skipping DNS pinning
+ if (envProxyMode && isMediaCDNTarget) {
+ await this.validateProxyResolution(targetHost, proxyUrl);
+ }
+
// PR #59007 behavior: skip DNS pinning for trusted proxy targets
if (envProxyMode && trustedProxyTarget) {
this.logger.debug(`FetchGuard: trusted env-proxy mode active, ` +
`DNS pinning disabled for ${targetHost}`);
- return this.directFetch(url, options);
+ // Route through proxy explicitly for CDN
+ return this.proxyFetch(url, options, proxyUrl);
}
return this.guardedFetch(url, options);Paso 3: Reconstruye y reinicia
cd /app
npm run build
sudo systemctl restart openclaw🧪 Verificación
Pasos de verificación
Paso 1: Confirmar versión
openclaw --version
# Expected: v2026.4.7 (if downgraded) or patched versionPaso 2: Validar carga de configuración
openclaw doctor --config
# Check for:
# ✓ Telegram proxy: http://100.80.46.108:8888
# ✓ DNS pinning: disabled or CDN hosts trusted
# ✓ Fetch guard: configuredPaso 3: Probar conectividad del proxy
# Test proxy to Telegram API
curl -x http://100.80.46.108:8888 \
https://api.telegram.org/bot$(cat TOKEN)/getMe \
-s | jq .
# Expected output:
{
"ok": true,
"result": {
"id": 123456789,
"is_bot": true,
"first_name": "YourBot",
...
}
}Paso 4: Probar conectividad CDN a través del proxy
# Test proxy to Telegram CDN (this was failing)
curl -x http://100.80.46.108:8888 \
https://cdn.telegram.org/_/files/placeholder/test.jpg \
-I -v 2>&1 | head -20
# Expected (before fix): HTTP/1.1 403 Forbidden
# Expected (after fix): HTTP/1.1 404 Not Found or 200 OKPaso 5: Enviar mensaje de medios de prueba
# Using Telegram bot API to get file path
curl -x http://100.80.46.108:8888 \
"https://api.telegram.org/bot$TOKEN/getUpdates" -s | jq '.result[].message.photo'
# If photos array is populated and contains file_id, CDN routing worksPaso 6: Monitorear logs de OpenClaw durante descarga de medios
# In one terminal, monitor logs
tail -f /var/log/openclaw/openclaw.log | grep -E "(media|cdn|fetch)"
# In another terminal, trigger a media message (send photo to bot)
# Expected log output after fix:
[INFO] TelegramMediaDownloader: downloading media via proxy
[DEBUG] FetchGuard: proxy route validated for cdn.telegram.org
[INFO] MediaDownload: completed successfully (234KB)Paso 7: Suite de pruebas automatizadas
# Run OpenClaw's built-in Telegram integration tests
openclaw test --channel telegram --media
# Expected output:
Telegram Media Integration Tests:
✓ Proxy configuration loaded
✓ CDN connectivity verified
✓ Image download: passed (1.2s)
✓ Voice message download: passed (0.8s)
✓ Document download: passed (3.1s)
✓ Sticker download: passed (0.2s)
5/5 tests passedLista de criterios de éxito
✓openclaw --versionmuestra v2026.4.7 o versión con parche✓Prueba de proxy acdn.telegram.orgdevuelve estado no-403✓El bot recibe y puede descargar fotos sin error "could not download media"✓Mensajes de voz y documentos se descargan exitosamente✓No hay mensajes de debugFetchGuard: DNS pinning disabledpara objetivos CDN
⚠️ Errores comunes
Trampas específicas del entorno
Redes de contenedores Docker
Cuando se ejecuta OpenClaw en Docker, el proxy debe ser accesible desde dentro del contenedor:
# WRONG: localhost inside container refers to container, not host
channels:
telegram:
proxy: "http://localhost:8888" # Fails in Docker!
# CORRECT: Use host.docker.internal or actual IP
channels:
telegram:
proxy: "http://host.docker.internal:8888"
# OR
channels:
telegram:
proxy: "http://172.17.0.1:8888"Problemas sutiles de Tailscale/VPN
La resolución DNS a través de Tailscale puede devolver resultados diferentes:
# Verify Tailscale exit node is NOT intercepting DNS
tsnet status | grep -i dns
# If DNS is routed through Tailscale, CDN IPs may differ from expected
# Fix: Disable MagicDNS or add explicit routes
sudo tailscale set --accept-dns=falseInterferencia de proxy del sistema macOS
macOS puede tener configuraciones de proxy a nivel de sistema en conflicto con la configuración de OpenClaw:
# Check for conflicting proxy settings
networksetup -getwebproxy "Wi-Fi"
networksetup -getsecurewebproxy "Wi-Fi"
# Disable if OpenClaw handles its own proxy
networksetup -setwebproxy "Wi-Fi" off
networksetup -setsecurewebproxy "Wi-Fi" offErrores de configuración
Barra diagonal final en URL del proxy
# WRONG: trailing slash causes parsing issues
"proxy": "http://100.80.46.108:8888/"
# CORRECT: no trailing slash
"proxy": "http://100.80.46.108:8888"Sensibilidad a mayúsculas en nombres de host
# WRONG: CDN domains are case-sensitive
"trustedHosts": ["CDN.TELEGRAM.ORG"]
# CORRECT: lowercase
"trustedHosts": ["cdn.telegram.org"]Variables de entorno en conflicto vs archivo de configuración
# Environment variables override config file (may cause confusion)
export OPENCLAW_TELEGRAM_PROXY="http://old.proxy:9999" # This wins!
# Check actual loaded config
openclaw doctor | grep -i proxyProblemas específicos de versión
Actualizar más allá de v2026.4.12
Si actualizas a v2026.4.13+ esperando la corrección, verifica que la corrección realmente fue incluida:
openclaw changelog --since v2026.4.12 | grep -i "dns\|cdn\|telegram\|media\|proxy"
# Look for: "Fix Telegram media download through proxy regression"Re-actualizar después de retroceder
Si retrocediste a v2026.4.7 y luego actualizas de nuevo:
# Config migration may not preserve all settings
# Re-verify after each upgrade
openclaw doctor --config | grep -A5 telegramErrores de diagnóstico
Ignorar niveles de log
# DEBUG logs are required to see FetchGuard behavior
# Default log level may hide critical information
# Set DEBUG logging
export LOG_LEVEL=debug
openclaw start
# Or in config:
{
"logging": {
"level": "debug",
"categories": ["network", "fetch-guard", "telegram"]
}
}Confundir fuentes de HTTP 403
# 403 can come from multiple sources:
# 1. Telegram CDN rejecting (actual bug)
# 2. Proxy rejecting (configuration)
# 3. Firewall rejecting (network)
# Differentiate by source:
curl -x http://100.80.46.108:8888 https://cdn.telegram.org -v 2>&1 | grep "< HTTP"
# If 403 here: proxy issue
# If 200 here but OpenClaw fails: FetchGuard issue🔗 Errores relacionados
Errores directamente relacionados
could not download media
Síntoma principal de esta regresión. Error genérico de TelegramMediaDownloader cuando el fetch CDN falla.FetchGuard: DNS pinning disabled
Log de debug indicando que el comportamiento de PR #59007 está activo. Esperado ver esto, pero no debería verse para objetivos CDN.HTTP 403 Forbidden from cdn.telegram.org
Rechazo del CDN de Telegram. Indica fallo de validación de enlace de IP.SSRF validation failed for target
Puede aparecer en logs si el pinado de DNS NO fue omitido (ruta de error alternativa).
Errores indirectamente relacionados
EADDRNOTAVAIL: cannot assign requested address
Error a nivel de red cuando la ruta del proxy está mal configurada. Indica fallo de enlace de socket.ECONNREFUSED
Proxy no accesible. Verifica que Tinyproxy está corriendo y la conexión Tailscale está activa.ETIMEDOUT
Tiempo de espera de conexión del proxy. Común con interferencia del GFW en ciertas rutas CDN.ENOTFOUND
Fallo de resolución DNS. Indica que el proxy no está manejando DNS como se espera.
Contexto histórico
- PR #62878 — "stop rejecting operator-configured proxy hostnames in SSRF-guarded fetches"
La mejora que hizo funcionar las descargas de medios basadas en proxy en v2026.4.7. - PR #59007 — "Network/fetch guard: skip target DNS pinning when trusted env-proxy mode is active"
El cambio que introdujo esta regresión. Optimización de seguridad bien intencionada que rompió el CDN de Telegram. - Issue #62878 (seguimiento) — Descargas de medios del CDN de Telegram regresaron después del cambio de pinado de DNS
El issue upstream que rastrea este problema específico.