From 06dd9b8ed864eb6668d42c497f0615e743da483a Mon Sep 17 00:00:00 2001 From: Joao Lisboa Date: Tue, 2 Dec 2025 14:28:56 -0300 Subject: [PATCH] fix: follow redirects when downloading Twilio media MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit node:https request() doesn't follow redirects by default, causing Twilio media URLs (which 302 to CDN) to save placeholder/metadata instead of actual images. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/media/store.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/media/store.ts b/src/media/store.ts index 82bb4c8c5..08546060b 100644 --- a/src/media/store.ts +++ b/src/media/store.ts @@ -1,3 +1,5 @@ +// ABOUTME: Media storage utilities - downloads from URLs and saves to disk +// ABOUTME: Handles both remote URLs (with redirect support) and local file paths import crypto from "node:crypto"; import { createWriteStream } from "node:fs"; import fs from "node:fs/promises"; @@ -48,9 +50,21 @@ async function downloadToFile( url: string, dest: string, headers?: Record, + maxRedirects = 5, ): Promise<{ headerMime?: string; sniffBuffer: Buffer; size: number }> { return await new Promise((resolve, reject) => { const req = request(url, { headers }, (res) => { + // Follow redirects + if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400) { + const location = res.headers.location; + if (!location || maxRedirects <= 0) { + reject(new Error(`Redirect loop or missing Location header`)); + return; + } + const redirectUrl = new URL(location, url).href; + resolve(downloadToFile(redirectUrl, dest, headers, maxRedirects - 1)); + return; + } if (!res.statusCode || res.statusCode >= 400) { reject(new Error(`HTTP ${res.statusCode ?? "?"} downloading media`)); return;