mckaywrigley/chatbot-ui · warning

Image must be less than ${imageSizeLimit / 1000000}MB

Error message

Image must be less than ${imageSizeLimit / 1000000}MB

What it means

Thrown by uploadProfileImage when the image File exceeds the hardcoded 2000000-byte (2MB) limit — the strictest of this app's three image limits (profile 2MB, assistant/message 6MB). It's a client-side check before any Supabase Storage call, so it fires synchronously with no network round-trip. The literal is defined in db/storage/profile-images.ts and is not env-configurable.

Source

Thrown at db/storage/profile-images.ts:13

import { supabase } from "@/lib/supabase/browser-client"
import { Tables } from "@/supabase/types"

export const uploadProfileImage = async (
  profile: Tables<"profiles">,
  image: File
) => {
  const bucket = "profile_images"

  const imageSizeLimit = 2000000 // 2MB

  if (image.size > imageSizeLimit) {
    throw new Error(`Image must be less than ${imageSizeLimit / 1000000}MB`)
  }

  const currentPath = profile.image_path
  let filePath = `${profile.user_id}/${Date.now()}`

  if (currentPath.length > 0) {
    const { error: deleteError } = await supabase.storage
      .from(bucket)
      .remove([currentPath])

    if (deleteError) {
      throw new Error("Error deleting old image")
    }
  }

  const { error } = await supabase.storage
    .from(bucket)
    .upload(filePath, image, {

View on GitHub (pinned to 81328b61d2)

Solutions

  1. Resize/compress the avatar client-side before upload — profile images render small, so re-encode to ~512px JPEG/WebP at 80% quality, which lands well under 2MB.
  2. Pre-check image.size in the picker UI and warn the user before submission.
  3. If 2MB is too strict for your users, raise the constant in db/storage/profile-images.ts.
  4. Accept HEIC but convert to JPEG client-side, since iPhone originals frequently exceed the cap.

Example fix

// before
const result = await uploadProfileImage(profile, rawFile) // 4MB photo -> throws

// after
const compressed = await resizeImage(rawFile, { maxDim: 512, quality: 0.8 })
const result = await uploadProfileImage(profile, compressed)
Defensive patterns

Strategy: validation

Validate before calling

const MAX = 2_000_000
if (image.size > MAX) {
  image = await resizeImage(image, { maxDim: 512, quality: 0.8 })
}
if (image.size > MAX) {
  toast.error("Profile image must be under 2MB")
  return
}

Type guard

const isValidProfileImage = (f: File): boolean =>
  f.type.startsWith("image/") && f.size <= 2_000_000

Try / catch

try {
  const { path, url } = await uploadProfileImage(profile, image)
} catch (e) {
  if (e instanceof Error && e.message.includes("less than 2MB")) {
    toast.error("Please choose a smaller image (under 2MB).")
  } else { throw e }
}

Prevention

When it happens

Trigger: Uploading a profile photo with image.size > 2000000: typical phone camera photos (3-15MB), PNG exports, or un-compressed canvas blobs. Any File object over 2MB triggers it immediately on submission.

Common situations: Users uploading full-resolution phone portraits; avatars cropped client-side but exported as lossless PNG at high resolution; developers confused because the same photo works for assistant images (6MB) but fails here; UI not pre-validating so the throw appears as a generic error toast.

Related errors


AI-assisted analysis of mckaywrigley/chatbot-ui@81328b61d2 (2026-08-27). Data as JSON: /api/errors/69951f140d9bce3d. Report an issue: GitHub.