gitroomhq/postiz-app · error · Error

Failed to fetch media: ${fileResponse.statusText}

Error message

Failed to fetch media: ${fileResponse.statusText}

What it means

While uploading media to Whop, the GET of the media file failed (non-ok status or empty body) and it throws with the fetch statusText. Fetch uses an SSRF-safe dispatcher, so internal/unreachable hosts also fail here.

Source

Thrown at libraries/nestjs-libraries/src/integrations/social/whop.provider.ts:280

              Authorization: `Bearer ${accessToken}`,
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              filename: fileName,
            }),
          },
          'create file record'
        )
      ).json();

      if (createFileResponse.upload_url) {
        const fileResponse = await fetch(item.path, {
          headers: { 'accept-encoding': 'identity' },
          // @ts-ignore - undici-only option; blocks SSRF to internal IPs
          dispatcher: getSsrfSafeDispatcher(),
        });
        if (!fileResponse.ok || !fileResponse.body) {
          throw new Error(`Failed to fetch media: ${fileResponse.statusText}`);
        }
        const uploadResponse = await fetch(createFileResponse.upload_url, {
          method: 'PUT',
          headers: {
            // Whop's own upload_headers win if they ever include a length.
            'Content-Length': String(contentLength),
            ...(createFileResponse.upload_headers || {}),
          },
          body: fileResponse.body,
          // Required by undici when streaming a request body.
          duplex: 'half',
        } as any);
        // A rejected PUT leaves the file pending forever - fail fast instead
        // of burning the ~9 minute status poll below.
        if (!uploadResponse.ok) {
          throw new BadBody(
            this.identifier,
            await uploadResponse.text().catch(() => '{}'),

View on GitHub (pinned to 0f1647f749)

Solutions

  1. curl the media URL from the worker machine to confirm reachability and a 200
  2. Re-add the media to the post so a fresh URL is generated
  3. If self-hosting, ensure the storage host is publicly resolvable/reachable from the orchestrator workers
  4. Check statusText/body in logs for the exact HTTP failure and fix that (403 vs 404 vs DNS)
Defensive patterns

Strategy: retry

Validate before calling

const testFetch = await fetch(mediaUrl, { method: 'HEAD', dispatcher: getSsrfSafeDispatcher() } as any);
if (!testFetch.ok) { /* refresh media URL or fail before upload */ }

Try / catch

catch (e) { if (e instanceof Error && e.message.startsWith('Failed to fetch media')) { /* transient network: retry with backoff; permanent if 403/404 */ } throw e; }

Prevention

When it happens

Trigger: GET on item.path returns 403/404 (expired presigned URL), DNS/network failure, or the URL points at a private IP which the SSRF-safe dispatcher blocks.

Common situations: Expired media URLs in pending posts; media host temporarily down; self-hosted storage on an internal hostname unreachable from the worker; presigned URL signed for a different request (e.g. GET disallowed).

Related errors


AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27). Data as JSON: /api/errors/a805abecde42a362. Report an issue: GitHub.