different-ai/openwork · warning · Error

Use a PNG or JPEG image.

Error message

Use a PNG or JPEG image.

What it means

createBrandAssetDraft validates brand assets before decoding them; the very first check rejects any File whose MIME type is not image/png or image/jpeg. This guards the image pipeline (createImageBitmap, dimension checks, upload) which only supports these two formats.

Source

Thrown at ee/apps/den-web/app/(den)/dashboard/_components/brand-appearance-screen.tsx:32

import { DenCard } from "../../_components/ui/card";
import { DenInput } from "../../_components/ui/input";
import { DenNotice } from "../../_components/ui/notice";
import { useOrgDashboard } from "../_providers/org-dashboard-provider";
import { EnterprisePlanNotice } from "./enterprise-plan-notice";

const BRAND_ASSET_MAX_BYTES = 2 * 1024 * 1024;

type BrandAssetKind = "logo" | "icon";

type BrandAssetDraft = {
  file: File;
  previewUrl: string;
  width: number;
  height: number;
};

async function createBrandAssetDraft(file: File, kind: BrandAssetKind): Promise<BrandAssetDraft> {
  if (file.type !== "image/png" && file.type !== "image/jpeg") throw new Error("Use a PNG or JPEG image.");
  if (file.size > BRAND_ASSET_MAX_BYTES) throw new Error("Use an image under 2 MB.");

  let image: ImageBitmap;
  try {
    image = await createImageBitmap(file);
  } catch {
    throw new Error("OpenWork could not decode that image.");
  }

  const { width, height } = image;
  image.close();
  if (width > 4096 || height > 4096) throw new Error("Use an image no larger than 4096×4096 pixels.");
  if (kind === "icon") {
    if (width < 64 || height < 64) throw new Error("Use a square icon at least 64×64 pixels.");
    if (width !== height) throw new Error("Use a square image for the app icon.");
  } else {
    const aspectRatio = width / height;
    if (width < 128 || height < 32) throw new Error("Use a wordmark at least 128×32 pixels.");

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Convert the image to PNG (best for icons) or JPEG in an image editor and re-upload.
  2. Never rely on file extension; check the reported MIME type in devtools — the guard uses file.type, the browser-sniffed type.
  3. Export as PNG from Figma/Sketch/Illustrator instead of SVG or WebP.
  4. If the file type reads as empty, re-save the file in an editor so the OS/browser assigns a proper MIME type.

Example fix

// before
<input type="file" accept="*/*" onChange={(e) => draft(e.target.files[0], "icon")} />
// after
<input type="file" accept="image/png,image/jpeg" onChange={(e) => draft(e.target.files[0], "icon")} />
Defensive patterns

Strategy: validation

Validate before calling

function isPngOrJpeg(file: File): boolean {
  return file.type === "image/png" || file.type === "image/jpeg";
}
if (!isPngOrJpeg(file)) { alert("Please choose a PNG or JPEG image."); return; }

Type guard

function isAcceptedImage(file: File): file is File & { type: "image/png" | "image/jpeg" } {
  return file.type === "image/png" || file.type === "image/jpeg";
}

Try / catch

try {
  const asset = await createBrandAssetDraft(file, kind);
} catch (error) {
  showToast(error instanceof Error ? error.message : "Could not use that image.");
}

Prevention

When it happens

Trigger: Uploading an app icon or wordmark file with MIME type image/webp, image/gif, image/svg+xml, image/avif, image/tiff, or any non-image type (e.g. a .png file renamed from a GIF whose stored type is image/gif, or type "" for unknown extensions).

Common situations: Designer hands over a WebP or SVG export; a file saved with a .png extension is actually GIF/WebP data so the browser reports the real MIME type; drag-and-drop of a PDF or HEIC screenshot from iOS; older Safari reporting empty file.type.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/4c033f1463771f9c. Report an issue: GitHub.