← Problem log

Supabase Storage upload returns 403

Last verified Oct 22
By the Bluhook team · Updated Nov 8, 2026
Exact text to search:
new row violates row-level security policy

1What happened

A scheduled job uploads 3 files to a Supabase Storage bucket every 15 minutes, overwriting the old ones. It used to upload with the service key. We moved it to a restricted user, so it could only write those 3 files. The user signed in fine (200), but every upload got 403 "new row violates row-level security policy". The page the files feed went stale.

It was down almost 4 days in all. First the job lost its old key, because we switched that key off without checking that this job still read it (Oct 18 to Oct 21). Then the 403 (Oct 21 to Oct 22).

What didn't help:

  1. Looking for a leftover code fix that never shipped: it had already shipped.

  2. Checking the sign-in: it returned 200, so the login was fine.

The cause: the storage policies were written but never applied to the database.

2What we did

  1. Applied 3 policies on storage.objects. The job overwrites files (an upsert, the x-upsert header), so it needs SELECT and UPDATE as well as INSERT.

    create policy "uploader insert" on storage.objects for insert to authenticated
      with check (bucket_id = '<bucket>' and name in ('<file1>','<file2>','<file3>')
                  and auth.uid() in ('<uploader-user-id>'));
    create policy "uploader select" on storage.objects for select to authenticated
      using (bucket_id = '<bucket>' and name in ('<file1>','<file2>','<file3>')
             and auth.uid() in ('<uploader-user-id>'));
    create policy "uploader update" on storage.objects for update to authenticated
      using (bucket_id = '<bucket>' and name in ('<file1>','<file2>','<file3>')
             and auth.uid() in ('<uploader-user-id>'))
      with check (bucket_id = '<bucket>' and name in ('<file1>','<file2>','<file3>')
                  and auth.uid() in ('<uploader-user-id>'));

    Choices that matter:

    1. Pin the user by its id, not its email.

    2. Allow exact file names only. If the bucket is public, a wider rule lets the user write anything anyone can read.

    3. No DELETE policy.

    4. No owner check. The existing files were written by the service key and have no owner, so an owner check refuses the first overwrite.

3How we know it worked

On Thu Oct 22, after the policies went in, a test upload returned 200. Every run in the job's log from 7:15 to 8:30 AM PT (6 runs, every 15 minutes) logged all 3 files uploaded. The same user writing a file name outside the list still got 403, so it can't write anything else.

4What you can take from it

If a Storage upload gets this 403 and the sign-in works, read the policies on storage.objects. An overwrite needs INSERT, SELECT and UPDATE.

supabasestoragerlspermissionsPublished Oct 22

Your free AI evaluation

Six free minutes with Sam. Your top 3 jobs for AI land in your inbox.

Show me my top 3 jobs

Questions people ask

Why does Supabase Storage upload give 403 "new row violates row-level security policy"?

In our case the storage policies were written but never applied to the database. The user signed in fine, but every upload was refused. If the sign-in works, read the policies on storage.objects.

What policies does a Supabase Storage overwrite need?

An overwrite, or upsert, needs INSERT, SELECT and UPDATE. We pinned the user by its id and allowed exact file names only, with no DELETE policy.

Should a Supabase Storage policy include an owner check?

Not in our case. The existing files were written by the service key and have no owner, so an owner check refuses the first overwrite.

Keep reading

Robb Lejuwaan

Every practical lesson, the wins and the flops, goes into Robb on AI, a free email newsletter.

Robb Lejuwaan, writer of Robb on AI
Get Robb on AI, free

Every lesson, win and flop, by email. Opens Substack.

Robb on AI is our free newsletter, written by Robb Lejuwaan.

We'll add you to Robb on AI, our free newsletter on Substack. Unsubscribe anytime. Privacy policy