mckaywrigley/chatbot-ui · error

Error downloading file

Error message

Error downloading file

What it means

Thrown by getFileFromStorage when createSignedUrl(filePath, 86400) on the 'files' bucket returns an error. Signed URL generation fails when the object doesn't exist at that path or the caller lacks SELECT access. The function does log the underlying error with the path before throwing, so server/console output shows the real StorageError.

Source

Thrown at db/storage/files.ts:53

}

export const deleteFileFromStorage = async (filePath: string) => {
  const { error } = await supabase.storage.from("files").remove([filePath])

  if (error) {
    toast.error("Failed to remove file!")
    return
  }
}

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

  if (error) {
    console.error(`Error uploading file with path: ${filePath}`, error)
    throw new Error("Error downloading file")
  }

  return data.signedUrl
}

View on GitHub (pinned to 81328b61d2)

Solutions

  1. Read the console error logged just before the throw — it includes filePath and the StorageError message.
  2. Verify the object exists at that path in the Supabase dashboard (Storage > files).
  3. Add a storage.objects SELECT policy on 'files' for authenticated users/owners.
  4. If objects can be deleted out-of-band, degrade gracefully (fallback placeholder) instead of throwing.

Example fix

// before
if (error) {
  console.error(`Error uploading file with path: ${filePath}`, error)
  throw new Error("Error downloading file")
}

// after
if (error) {
  console.error(`Error signing URL for path: ${filePath}`, error)
  return null // let the caller show a fallback
}
Defensive patterns

Strategy: fallback

Validate before calling

const { data } = await supabase.storage.from("files").list(folder(path))
if (!data?.some(o => o.name === name(path))) return placeholderUrl

Type guard

const hasSignedUrl = (r: unknown): r is { signedUrl: string } =>
  !!r && typeof (r as { signedUrl?: string }).signedUrl === "string"

Try / catch

try {
  url = await getFileFromStorage(path)
} catch {
  url = null // show 'file unavailable' in UI instead of crashing the message list
}

Prevention

When it happens

Trigger: Requesting a signed URL for a path that was deleted (deleteFileFromStorage ran first), a stale base64(file_id) path after a record was recreated, a path from a different environment's bucket, or a private 'files' bucket with no SELECT policy for the requesting role.

Common situations: Chat history referencing attachments whose storage objects were removed; multi-environment DBs sharing rows but not buckets; missing storage.objects SELECT policy; expired session when policies require authenticated users.

Related errors


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