makeplane/plane · warning · Error

Invalid file type. Please select an image.

Error message

Invalid file type. Please select an image.

What it means

Thrown by the cover-image upload helper after a local image URL is fetched. The code reads the response blob and checks that its MIME type starts with 'image/'; anything else (HTML error pages, octet-stream, PDFs) is rejected before being wrapped into a File and uploaded via fileService. The post-fetch check exists because URL extensions and filenames are unreliable signals of real content type.

Source

Thrown at apps/web/helpers/cover-image.helper.ts:221

    entityIdentifier: string;
    entityType: EFileAssetType;
    isUserAsset?: boolean;
  }
): Promise<string> => {
  const { workspaceSlug, entityIdentifier, entityType, isUserAsset = false } = uploadConfig;

  // Fetch the local image
  const response = await fetch(imageUrl);

  if (!response.ok) {
    throw new Error(`Failed to fetch image: ${response.statusText}`);
  }

  const blob = await response.blob();

  // Validate it's actually an image
  if (!blob.type.startsWith("image/")) {
    throw new Error("Invalid file type. Please select an image.");
  }

  const fileName = imageUrl.split("/").pop()?.split("?")[0] || "image.jpg";
  const file = new File([blob], fileName, { type: blob.type });

  // Upload based on context
  if (isUserAsset) {
    const uploadResult = await fileService.uploadUserAsset(
      {
        entity_identifier: entityIdentifier,
        entity_type: entityType,
      },
      file
    );
    return uploadResult.asset_url;
  } else {
    if (!workspaceSlug) {
      throw new Error("Workspace slug is required for workspace asset upload");

View on GitHub (pinned to 1c8a60f858)

Solutions

  1. Open the URL directly in a browser or run `curl -I <url>` and confirm the Content-Type header is image/*.
  2. If the server returns application/octet-stream for real images, fix the upstream/CDN to send the correct image MIME.
  3. Add a HEAD preflight before calling the helper and show a user-facing error if Content-Type is not image/*.
  4. For SVGs, ensure the source emits image/svg+xml; if support is unwanted, block it explicitly upstream.

Example fix

// before
await uploadCoverImageHelper(imageUrl, ...);

// after
const probe = await fetch(imageUrl, { method: 'HEAD' });
const ct = probe.headers.get('content-type') ?? '';
if (!ct.startsWith('image/')) {
  throw new Error(`URL is not an image (got ${ct})`);
}
await uploadCoverImageHelper(imageUrl, ...);
Defensive patterns

Strategy: validation

Validate before calling

async function isImageUrl(url: string): Promise<boolean> {
  const probe = await fetch(url, { method: 'HEAD' });
  const ct = probe.headers.get('content-type') ?? '';
  return probe.ok && ct.startsWith('image/');
}

Type guard

function isImageBlob(blob: Blob): boolean {
  return typeof blob.type === 'string' && blob.type.startsWith('image/');
}

Try / catch

try {
  await uploadCoverImageHelper(url, ctx);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Invalid file type')) {
    notifyUser('That URL is not an image. Pick an image file.');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the cover-image helper with a URL whose response 'Content-Type' is not an image MIME (e.g. text/html for an SPA route or error page, application/octet-stream from a misconfigured CDN, application/pdf). Also when the blob decodes without a recognized type (blob.type === '').

Common situations: Passing a frontend route or 404 HTML page instead of the raw asset URL; CDN/proxy stripping Content-Type or serving binary/octet-stream; file still transcoding on the server so the link returns JSON metadata; cross-origin response where the gateway rewrites the type.

Related errors


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/8b1761fc92e999f6. Report an issue: GitHub.