Telegram Media Download Fails Through HTTP Proxy After v2026.4.8 Upgrade
Regression caused by DNS pinning bypass in trusted env-proxy mode disrupts Telegram CDN media downloads while text messages continue working normally.
๐ Symptoms
Primary Error Manifestation
Telegram media downloads fail with a generic error message:
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)Affected Operations
The following media types fail through configured HTTP proxy:
- Images (photos sent/received in chats)
- Voice messages (.ogg audio files)
- Video notes
- Document attachments (files via CDN)
Operations That Continue Working
- Text message sending/receiving
- Channel management commands
- Bot command processing
- Sticker retrieval (small files)
Diagnostic Indicators
# 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 CDNEnvironment Context
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๐ง Root Cause
Architectural Background: The Conflicting PRs
The regression stems from an interaction between two pull requests with opposing goals:
PR #62878 (v2026.4.7 area)
Goal: Allow operator-configured proxy hostnames in SSRF-guarded fetches
Effect: Media downloads through configured proxies began working correctly
Mechanism: Modified FetchGuard to trust proxy hostnames specified in channels.telegram.proxy
PR #59007 (v2026.4.8)
Goal: Skip target DNS pinning when trusted env-proxy mode is active
Effect: Disrupted the mechanism that PR #62878 relied upon
Mechanism: When trusted env-proxy mode is detected, DNS pinning checks are bypassed for the proxy target
Failure Sequence Analysis
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 upCode-Level Root Cause
The issue resides in 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);
} The bug: When trusted env-proxy mode is detected, the code assumes the proxy will handle DNS resolution correctly but performs no validation. Telegram CDN domains require specific IP binding validation that the proxy bypass breaks.
Why Text Messages Still Work
Telegram API calls (api.telegram.org) use a different endpoint classification:
- API endpoints are whitelisted in `TRUSTED_API_HOSTS`
- They bypass DNS pinning entirely via separate code path
- Media CDN endpoints (`cdn.telegram.org`, `*.t.me`) have stricter IP validation
๐ ๏ธ Step-by-Step Fix
Option A: Configuration-Based Workaround (Immediate)
Add explicit CDN domain handling to bypass the regression without code changes:
Step 1: Locate your openclaw.json configuration
# Standard locations
macOS/Linux: ~/.config/openclaw/openclaw.json
Docker: /app/config/openclaw.json
Systemd service: /etc/openclaw/openclaw.jsonStep 2: Add CDN domains to trusted hosts
{
"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"]
}
}
}
}Step 3: Restart OpenClaw
# Systemd
sudo systemctl restart openclaw
# Docker
docker restart openclaw
# Direct process
pkill -f openclaw && openclawOption B: Disable DNS Pinning for Media Downloads (Targeted)
If Option A doesn’t resolve the issue, temporarily disable DNS pinning for Telegram media:
Step 1: Create environment override file
cat > /etc/openclaw/overrides/telegram-media.env << 'EOF'
OPENCLAW_TELEGRAM_DNS_PINNING=disabled
OPENCLAW_FETCH_GUARD_STRICT_MODE=false
EOFStep 2: Verify environment is loaded
# Check that OpenClaw reads the override
openclaw doctor --env | grep -i telegram
# Expected output:
# TELEGRAM_DNS_PINNING=disabled
# FETCH_GUARD_STRICT_MODE=falseStep 3: Test media download
# Send a test image to your bot and check logs
tail -f /var/log/openclaw/openclaw.log | grep -i mediaOption C: Downgrade to Last Known Good Version (Definitive)
# 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.7Option D: Patch the Fetch Guard (Permanent Fix Pending)
Wait for the fix in v2026.4.13+ or apply the patch manually:
Step 1: Locate fetch-guard.ts
find /app -name "fetch-guard.ts" -type f 2>/dev/null
# Output: /app/src/network/fetch-guard.tsStep 2: Apply targeted patch
# 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);Step 3: Rebuild and restart
cd /app
npm run build
sudo systemctl restart openclaw๐งช Verification
Verification Steps
Step 1: Confirm Version
openclaw --version
# Expected: v2026.4.7 (if downgraded) or patched versionStep 2: Validate Configuration Loading
openclaw doctor --config
# Check for:
# โ Telegram proxy: http://100.80.46.108:8888
# โ DNS pinning: disabled or CDN hosts trusted
# โ Fetch guard: configuredStep 3: Test Proxy Connectivity
# 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",
...
}
}Step 4: Test CDN Connectivity Through 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 OKStep 5: Send Test Media Message
# 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 worksStep 6: Monitor OpenClaw Logs During Media Download
# 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)Step 7: Automated Test Suite
# 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 passedSuccess Criteria Checklist
โopenclaw --versionshows v2026.4.7 or patched versionโProxy test tocdn.telegram.orgreturns non-403 statusโBot receives and can download photos without "could not download media" errorโVoice messages and documents download successfullyโNoFetchGuard: DNS pinning disableddebug messages for CDN targets
โ ๏ธ Common Pitfalls
Environment-Specific Traps
Docker Container Networking
When running OpenClaw in Docker, the proxy must be reachable from inside the container:
# 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"Tailscale/VPN Subtle Issues
DNS resolution through Tailscale may return different results:
# 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=falsemacOS System Proxy Interference
macOS may have system-level proxy settings conflicting with OpenClaw config:
# 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" offConfiguration Pitfalls
Trailing Slash in Proxy URL
# WRONG: trailing slash causes parsing issues
"proxy": "http://100.80.46.108:8888/"
# CORRECT: no trailing slash
"proxy": "http://100.80.46.108:8888"Case Sensitivity in Hostnames
# WRONG: CDN domains are case-sensitive
"trustedHosts": ["CDN.TELEGRAM.ORG"]
# CORRECT: lowercase
"trustedHosts": ["cdn.telegram.org"]Conflicting Environment Variables vs Config File
# 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 proxyVersion-Specific Gotchas
Upgrading Past v2026.4.12
If upgrading to v2026.4.13+ expecting fix, verify the fix was actually included:
openclaw changelog --since v2026.4.12 | grep -i "dns\|cdn\|telegram\|media\|proxy"
# Look for: "Fix Telegram media download through proxy regression"Re-Upgrading After Downgrade
If you downgraded to v2026.4.7 then upgrade again:
# Config migration may not preserve all settings
# Re-verify after each upgrade
openclaw doctor --config | grep -A5 telegramDiagnostic Mistakes
Ignoring Log Levels
# 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"]
}
}Confusing HTTP 403 Sources
# 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๐ Related Errors
Directly Related Errors
could not download media
Primary symptom of this regression. Generic error from TelegramMediaDownloader when CDN fetch fails.FetchGuard: DNS pinning disabled
Debug log indicating PR #59007 behavior is active. Expected to see this, but should not see it for CDN targets.HTTP 403 Forbidden from cdn.telegram.org
Telegram CDN rejection. Indicates IP binding validation failure.SSRF validation failed for target
May appear in logs if DNS pinning was NOT skipped (alternative error path).
Indirectly Related Errors
EADDRNOTAVAIL: cannot assign requested address
Network-level error when proxy route is misconfigured. Indicates socket binding failure.ECONNREFUSED
Proxy not reachable. Check Tinyproxy is running and Tailscale connection is active.ETIMEDOUT
Proxy connection timeout. Common with GFW interference on certain CDN routes.ENOTFOUND
DNS resolution failure. Indicates proxy is not handling DNS as expected.
Historical Context
- PR #62878 โ "stop rejecting operator-configured proxy hostnames in SSRF-guarded fetches"
The improvement that made proxy-based media downloads work in v2026.4.7. - PR #59007 โ "Network/fetch guard: skip target DNS pinning when trusted env-proxy mode is active"
The change that introduced this regression. Well-intentioned security optimization that broke Telegram CDN. - Issue #62878 (follow-up) โ Telegram CDN media downloads regressed after DNS pinning change
The upstream issue tracking this specific problem.