gitroomhq/postiz-app · error · HttpException

Unsupported file type.

Error message

Unsupported file type.

What it means

After downloading, uploadsFromUrl sniffs the file's magic bytes with fileTypeFromBuffer and checks the detected MIME against a whitelist (PUBLIC_API_ALLOWED_MIME). If detection fails or the type isn't whitelisted, it throws 400 'Unsupported file type.' — the extension in the URL is irrelevant, only actual content is trusted.

Source

Thrown at apps/backend/src/public-api/routes/v1/public.integrations.controller.ts:139

    }
    if (!response.ok) {
      throw new HttpException({ msg: 'Failed to fetch URL' }, 400);
    }

    // Guard against OOM: bail out before buffering the whole body into memory.
    // Content-Length may be absent or wrong, so we re-check the real size after
    // download too. The type isn't known yet (sniffed below), so the pre-check
    // uses the largest allowed cap (video).
    const maxDownloadSize = getMaxSize('video/mp4');
    const declaredSize = Number(response.headers.get('content-length'));
    if (declaredSize && declaredSize > maxDownloadSize) {
      throw new HttpException({ msg: 'File is too large.' }, 400);
    }

    const buffer = Buffer.from(await response.arrayBuffer());
    const detected = await fileTypeFromBuffer(buffer);
    if (!detected || !PUBLIC_API_ALLOWED_MIME.has(detected.mime)) {
      throw new HttpException({ msg: 'Unsupported file type.' }, 400);
    }

    if (buffer.length > getMaxSize(detected.mime)) {
      throw new HttpException({ msg: 'File is too large.' }, 400);
    }

    const mimetype = detected.mime;
    const ext = detected.ext;

    const getFile = await this.storage.uploadFile({
      buffer,
      mimetype,
      size: buffer.length,
      path: '',
      fieldname: '',
      destination: '',
      stream: new Readable(),
      filename: '',

View on GitHub (pinned to 0f1647f749)

Solutions

  1. Download the file locally and run `file` or a magic-byte checker to confirm its real type
  2. Convert/re-encode the asset to a whitelisted format (standard jpeg/png/mp4 etc.)
  3. If the URL returns an HTML error/login page, fix the link or make the asset directly fetchable (public, no auth wall)
  4. Check PUBLIC_API_ALLOWED_MIME in the codebase for the exact accepted list

Example fix

// before
{ "url": "https://example.com/photo.heic" }

// after
{ "url": "https://example.com/photo.jpg" } // re-encoded to a whitelisted format
Defensive patterns

Strategy: type-guard

Validate before calling

const res = await fetch(url);
const buf = Buffer.from(await res.arrayBuffer());
const sig = buf.subarray(0, 12).toString('hex');
const known = { 'ffd8ff': 'jpeg', '89504e47': 'png', '66747966': 'mp4' /* etc */ };
if (!Object.keys(known).some(k => sig.startsWith(k))) throw new Error('Not a whitelisted media type');

Type guard

import { fileTypeFromBuffer } from 'file-type';
const isAllowedMediaType = async (buf: Buffer): Promise<boolean> => {
  const t = await fileTypeFromBuffer(buf);
  return !!t && ALLOWED_MIME.has(t.mime); // mirror PUBLIC_API_ALLOWED_MIME
};

Try / catch

try {
  await api.uploadsFromUrl({ url });
} catch (e) {
  if (e?.response?.data?.msg === 'Unsupported file type.') {
    // re-encode the asset and retry; don't trust the file extension
  }
}

Prevention

When it happens

Trigger: POST /public/v1/uploads/from-url with a URL serving an SVG, GIF variants, HEIC, text/html (an error page), or any binary whose magic bytes aren't in the whitelist. Also happens when the URL returns an HTML login/error page instead of the media, or when detection fails on truncated/empty bodies.

Common situations: Pointing at an HTML page behind auth rather than the asset; unusual formats like HEIC/AVIF/WebP variants not in the allowlist; renamed files (a .jpg that's actually a PDF); empty responses from misconfigured CDNs.

Related errors


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