Short answer
Upload each new file to a unique path, for example with a UUID or timestamp in the name. If the app really should replace the file, pass upsert: true and add SELECT and UPDATE policies on storage.objects next to the INSERT policy.
Rather not fix it yourself? App Fix Ticket starts with a fixed-price diagnosis, then a fixed price for the fix before any fix work.
Why the upload collides, and the fix
01
Every upload uses the same file name
“When uploading a file to a path that already exists, the default behavior is to return a 400 Asset Already Exists error.” Supabase docs, Standard uploads (overwriting files)
Names like avatar.png or the original file name collide on the second upload. Put a unique part in the path.
const path = `${user.id}/${crypto.randomUUID()}.png`; const { data, error } = await supabase.storage .from('avatars') .upload(path, file);02
The app means to replace the file
“Use x-upsert:true header to overwrite the resource.” Supabase docs, Storage error codes (ResourceAlreadyExists)
For a profile photo that should overwrite the old one, pass upsert: true in the upload options.
await supabase.storage .from('avatars') .upload(`${user.id}/avatar.png`, file, { upsert: true });03
upsert is on, but the policies only allow INSERT
“To allow overwriting files using the upsert functionality you will need to additionally grant SELECT and UPDATE permissions.” Supabase docs, Storage access control
Overwriting reads and updates the existing row in storage.objects, so an INSERT-only policy is not enough. Add SELECT and UPDATE policies with the same folder check.
04
Users still see the old file
“Uploading a file to a new path is the recommended way to avoid propagation delays and stale content.” Supabase docs, Standard uploads
Also check: an overwritten file can stay cached at the edge for a while. A new path per version avoids stale images.
Checked against the vendor’s docs on : Supabase docs: Storage error codes; Supabase docs: Standard uploads; Supabase docs: Storage access control.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.See scope and start
“Very responsive, attentive to fixing bugs, and completed the project as needed! Thank you!”
Related: Supabase Storage invalid key · Supabase Storage bucket not found · Supabase Storage entity too large · Supabase Storage MIME type error