April 14, 2026 โ€ข Version: v2026.4.8 - v2026.4.12

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 CDN

Environment 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 up

Code-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.json

Step 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 && openclaw

Option 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
EOF

Step 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=false

Step 3: Test media download

# Send a test image to your bot and check logs
tail -f /var/log/openclaw/openclaw.log | grep -i media

Option 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.7

Option 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.ts

Step 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 version

Step 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: configured

Step 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 OK

Step 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 works

Step 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 passed

Success Criteria Checklist

  • โœ“ openclaw --version shows v2026.4.7 or patched version
  • โœ“ Proxy test to cdn.telegram.org returns non-403 status
  • โœ“ Bot receives and can download photos without "could not download media" error
  • โœ“ Voice messages and documents download successfully
  • โœ“ No FetchGuard: DNS pinning disabled debug 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=false

macOS 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" off

Configuration 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 proxy

Version-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 telegram

Diagnostic 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
  • 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).
  • 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.

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.