mckaywrigley/chatbot-ui · error

Error downloading message image

Error message

Error downloading message image

What it means

Thrown by getMessageImageFromStorage when createSignedUrl(filePath, 86400) on the 'message_images' bucket returns an error. Supabase fails signed-url creation when the object doesn't exist at filePath or the caller's role can't SELECT it. Unlike getAssistantImageFromStorage, this version throws to the caller rather than swallowing the error.

Source

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

  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. Verify the object exists at the logged path in the Supabase storage dashboard.
  2. Add a storage.objects SELECT policy on message_images for authenticated users (or make the bucket public if appropriate).
  3. Wrap the call and fall back to a placeholder image when signing fails, so one bad message doesn't break the whole chat render.
  4. Audit for code paths that delete message images while messages still reference them.

Example fix

// before
const url = await getMessageImageFromStorage(path)

// after
let url: string | null = null
try {
  url = await getMessageImageFromStorage(path)
} catch {
  url = null // render placeholder
}
Defensive patterns

Strategy: fallback

Validate before calling

if (!filePath) return placeholderUrl

Type guard

const isSignablePath = (p: unknown): p is string =>
  typeof p === "string" && p.length > 0 && !p.startsWith("/")

Try / catch

let url: string | null = null
try {
  url = await getMessageImageFromStorage(path)
} catch {
  url = null // render image placeholder in the chat bubble
}

Prevention

When it happens

Trigger: Loading a chat message whose image path was deleted from storage, was written to a different bucket/project, or isn't covered by a storage.objects SELECT policy for the current (possibly anon) role; passing an empty or malformed path string.

Common situations: Old messages referencing purged objects (retention jobs, manual bucket cleanup); private bucket without SELECT policies; rendering history while logged out where anon role can't read; environment mismatch between DB rows and storage bucket contents.

Related errors


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