mckaywrigley/chatbot-ui · error

Error uploading image

Error message

Error uploading image

What it means

Thrown by uploadMessageImage when supabase.storage.from("message_images").upload(path, image, { upsert: true }) returns an error. The StorageError's real message (bucket not found, RLS violation, duplicate, size/type rejection) is masked by this generic string. Because upsert is enabled, an existing object at the same path must be updatable, not just insertable.

Source

Thrown at db/storage/message-images.ts:17

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

export const uploadMessageImage = async (path: string, image: File) => {
  const bucket = "message_images"

  const imageSizeLimit = 6000000 // 6MB

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

  const { error } = await supabase.storage.from(bucket).upload(path, image, {
    upsert: true
  })

  if (error) {
    throw new Error("Error uploading image")
  }

  return path
}

export const getMessageImageFromStorage = async (filePath: string) => {
  const { data, error } = await supabase.storage
    .from("message_images")
    .createSignedUrl(filePath, 60 * 60 * 24) // 24hrs

  if (error) {
    throw new Error("Error downloading message image")
  }

  return data.signedUrl
}

View on GitHub (pinned to 81328b61d2)

Solutions

  1. Log the error object to surface error.message ('Bucket not found', 'row-level security', 'Duplicate').
  2. Create the 'message_images' bucket and grant INSERT + UPDATE policies to the object owner (auth.uid() = (storage.foldername(name))[1]).
  3. Ensure the user has a fresh authenticated session before uploading.
  4. Confirm the client is pointed at the project containing the bucket (check NEXT_PUBLIC_ env vars).

Example fix

// before
if (error) {
  throw new Error("Error uploading image")
}

// after
if (error) {
  throw new Error(`Error uploading image to message_images: ${error.message}`)
}
Defensive patterns

Strategy: retry

Validate before calling

const { data: s } = await supabase.auth.getSession()
if (!s.session) throw new Error("Sign in before sending images")

Type guard

const isUploadedImage = (r: unknown): r is string =>
  typeof r === "string" && r.length > 0

Try / catch

for (let i = 0; i < 3; i++) {
  const { error } = await supabase.storage.from("message_images").upload(path, image, { upsert: true })
  if (!error) break
  if (!/network|fetch|timeout/i.test(error.message)) throw new Error(error.message)
  await new Promise(r => setTimeout(r, 400 * 2 ** i))
}

Prevention

When it happens

Trigger: The 'message_images' bucket doesn't exist in the project; the caller's role fails an INSERT or UPDATE storage policy for the given path; session expired so requests run as anon; or the path collides with an object owned by another user and upsert is denied.

Common situations: Bucket created in dev but not prod (or vice versa); storage policies only granting INSERT so upsert-overwrite fails with 'Duplicate'; missing auth session refresh in long-lived tabs; wrong supabase project env vars after forking the app.

Related errors


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