FixesSupabase errors and fixes
Sign-in and email problems, row level security, queries that return nothing, Edge Functions, storage and the database. Each page is checked against Supabase’s own docs and says what to try first.
28 Supabase fixes. Also see Supabase RLS policy checker, Supabase grants and RLS checker and Supabase RLS fixes.
- Supabase setup questionHow do I get a Supabase staging environment and stop testing on production?Use Supabase Branching: a persistent branch for staging, or a preview branch for each pull request. A branch connected to GitHub builds its database structure from the migrations in your repository; every branch starts with no data by default and gets placeholder sign-in secrets. So get your schema into migrations first, then add a seed file and branch secrets. Watch the cost: branches bill by the hour and aren’t covered by the Spend Cap.
- Supabase StorageMy Supabase storage bucket is public. Are the files exposed?A public bucket skips access control for reading files: anyone with a file’s URL can open it. Uploads, deletes and moves are still checked by your policies. Keep public buckets for files meant to be public, move private files to a private bucket with row-level security policies, and serve them through signed URLs or signed-in downloads.
- Supabase RealtimeSupabase Realtime is subscribed, but no changes arriveFirst check the table is in the supabase_realtime publication, since Realtime ignores tables that aren’t. Then check row-level security: Realtime applies it as the subscribing user, so on a table with RLS it only sends changes that user is allowed to see, and SUBSCRIBED doesn’t mean they can. If events arrive but old values are empty, that’s replica identity. Last, check the table name, schema and filter in the subscription.
- Supabase databaseSupabase: too many connections, or remaining connection slots are reservedYour database has a connection limit set by its compute size, and something is opening more connections than it allows. In serverless or edge functions, connect through Supabase’s shared pooler in transaction mode, create the client once, and set its pool to 1 connection. If you’re already pooling and still at the limit, a larger compute size allows more connections.
- Supabase Auth session bugSupabase logs users out when they refresh the pageIn a Next.js or other server-rendered app, use the @supabase/ssr clients so the session lives in cookies, and add the middleware (proxy.ts on Next.js 16) that refreshes it on every request. In a browser-only app, create one Supabase client with persistSession on, and wait for the stored session to load before your route guard sends anyone to the login page.
- Supabase migration errorWhy does supabase db push fail, and how do I fix the migration history?Supabase keeps a list of the migrations it has run on each database and compares it with the files in supabase/migrations. When the two disagree, db push stops. Back up the live database, link the project with supabase link, then run supabase migration list to see where they split. If someone changed the live database by hand, run supabase db pull to capture that as a migration. If only the list is wrong, migration repair fixes the list without running any SQL.
- Supabase database errornew row violates row-level security policy for tableThe table has RLS on and no INSERT policy that this row passes. Add one whose WITH CHECK matches the row, usually user_id = auth.uid(), and make sure the insert sets user_id for a signed-in user. If you call .select() after the insert, or use upsert, the table also needs a SELECT policy the new row passes. Don’t fix it by turning RLS off.
- Supabase Auth email bugSupabase confirmation and reset emails link to localhost:3000Change the Site URL in Supabase under Authentication, URL Configuration, from http://localhost:3000 to your live address. Supabase uses it when your code doesn’t pass a redirect URL. If your code does pass one, that exact URL must also be on the Redirect URLs allow list.
- Sign in with Apple errorWhy does Sign in with Apple return invalid_client?invalid_client means Apple couldn’t find the client ID you sent, or the client secret is invalid or expired. On the web, the client ID must be your Services ID, exactly as registered. In Supabase, list the Services ID first under Client IDs. The secret key for the OAuth flow has to be regenerated every 6 months from your .p8 signing key; an expired one fails with invalid_client too.
- Supabase Edge Functions errorSupabase Edge Function call blocked by CORS policyThe function has to answer the browser’s OPTIONS preflight request and include CORS headers on every response, errors included. Supabase’s wrapper does this for you, or you can import corsHeaders from supabase-js. If it still fails, read the response status: an error can make the browser report CORS when the real problem is something else.
- Supabase Edge Function errorWhy does my Supabase Edge Function time out with a 504 or 546?A 504 means the function didn’t send a response within the 150-second request timeout, usually because of a slow query, a slow outside API or a loop. A 546, Supabase’s own resource-limit code, means it ran out of memory or CPU time, and the function’s logs say which. Speed up or put timeouts on slow calls, split heavy work into smaller jobs, and answer first when the caller doesn’t need to wait.
- Supabase key leakMy Supabase service_role key is exposedTreat the key as compromised. Fix how it leaked first, or the new key leaks the same way. Then create a new secret key, replace the old one everywhere, confirm nothing still uses it, and retire the old key. If it was a legacy service_role key, also revoke the legacy JWT secret. If someone is actively using the leaked key, retire it right away and do the leak fix afterwards. Retiring legacy keys also switches off the anon key, so the site breaks until your apps move to a publishable key. Then check your Supabase logs and tables for changes you didn’t make.
- Supabase Auth email errorSupabase “email rate limit exceeded” on sign-upSupabase’s built-in email service is for trying things out. It only sends to members of your project’s team, and Supabase currently limits it to 2 messages an hour, a number it says can change without notice. Set up custom SMTP with an email provider. Supabase then starts you at 30 messages an hour, which you can raise on the Rate Limits page before a launch.
- Supabase Auth errorSupabase sign-in fails with “Email not confirmed”With Confirm Email on, which is the default on hosted Supabase projects, users have to click the link in the confirmation email before their first sign-in. If they never got the email, check sending first: the default email service allows 2 emails per hour on a best-effort basis, so set up custom SMTP for production. Make sure the link goes to your real site rather than localhost:3000, then resend the confirmation with supabase.auth.resend().
- Supabase Auth errorSupabase sign-up fails with “Database error saving new user”Supabase lists three common causes: a trigger or trigger function on auth.users that has an error, a constraint on auth.users that isn’t met, or Prisma breaking the permissions on that table. Open the Auth logs first, then the Postgres logs, for the exact error. A frequent one is a handle_new_user trigger inserting into a profiles table that doesn’t exist.
- Supabase Edge Functions errorSupabase: “Edge Function returned a non-2xx status code”Your function ran but returned an error status (4xx or 5xx). In supabase-js that is a FunctionsHttpError, and the function’s own error message is in error.context: read it with await error.context.json(). Then check the function’s logs in the Logs tab of the Supabase dashboard, fix what the function reports, and have it return a clear status code and message.
- Supabase Auth errorSupabase users logged out with “Invalid Refresh Token”Each Supabase refresh token can be exchanged only once, for a new access and refresh token pair. Reuse is allowed within a 10-second interval by default, or when the parent of the active token is used; otherwise Supabase treats the whole session as terminated and revokes all its refresh tokens. Look for two places refreshing with the same token, such as the server and the browser, or an app restoring an older saved session.
- Supabase Auth errorSupabase: “Email link is invalid or has expired” (otp_expired)Supabase says the most common cause is email prefetching: a mail client or security tool, such as Safe Links in Microsoft Defender for Office 365, opens the link to check it and uses up the one-time token. Stop the link from consuming the token: put the 6-digit code {{ .Token }} in the email template, or link to a page of your own where the user enters it.
- Supabase email problemWhy do my Supabase sign-up emails go to spam, and how do I fix it?Send through your own email provider (custom SMTP in Supabase, such as Resend or Twilio SendGrid) from your own domain, and verify that domain with the provider so SPF and DKIM pass. Add a DMARC record, starting with p=none, or edit the one you already have. Keep the From: address on the domain you verified, ideally the same domain as your website, and leave open and click tracking off for sign-up emails.
- Supabase project pausedMy Supabase project is paused and my app is downOpen the project in the Supabase dashboard and click Resume project. Supabase says a paused project can be restored for up to 1 year after it was paused. Once it’s back, check your tables and row counts and that the app connects again. For an app with real users, move the project to a paid plan: Supabase doesn’t pause paid projects.
- Supabase key questionIs it safe that my Supabase anon key is in my frontend code?Yes, if row-level security is on. Supabase calls the anon key the legacy version of the publishable key, which it says is safe to expose online, and anyone can read it in your code. Row-level security decides what that key can reach, so turn it on for every table and give each table policies. The key that must never be public is the secret or service_role key.
- Supabase security questionI turned on RLS in Supabase. Is my data safe now?Not necessarily. Turning on RLS is the first step, not the last. Check four things: that signed-out visitors don’t have permissions on tables they don’t need, that any view over a protected table follows the table’s rules, that no rule lets everyone read private data, and that the admin key (secret or service_role) never reaches the browser. Then test the rules by signing in as two different users.
- Supabase published app errorSupabase “TypeError: Failed to fetch” on a published appOpen the browser’s Network tab, reload, and look at the failed request. If its address contains undefined, the Supabase URL never reached the published build. If the address is right, check whether the project is paused, whether an Edge Function is failing its CORS check, and whether a browser extension is blocking the call.
- Supabase API errorSupabase “Could not find the table … in the schema cache” (PGRST205)Supabase’s API can’t see a table with that name. First check the table exists in the project your app’s URL points at, since a migration that never ran, or a staging project, would leave it missing. If it exists, reload the schema cache with NOTIFY pgrst, 'reload schema'; in the SQL Editor. If it lives outside the public schema, expose that schema or name it in the query.
- Supabase Auth errorSupabase “provider_disabled”: the sign-in provider is offEnable the provider on its page in the hosted project’s dashboard, with the client ID and secret from the provider. For local development, Supabase configures the provider in supabase/config.toml instead; we also restart the local stack after editing it. For email or phone sign-up, check that those providers are on as well.
- Supabase Auth errorSupabase “Invalid login credentials” when users sign inCheck error.code. If it is invalid_credentials, Supabase doesn’t say which problem it is: it can mean a wrong password, no account with that email, or an account that only signs in through a social login. Send a password reset if the user has a password account, or point them to the button they signed up with. If the code is email_not_confirmed, it’s a different problem.
- Supabase query errorSupabase PGRST116: .single() got no row, or more than oneIf the row may not exist yet, such as a profile for a brand-new user, use .maybeSingle(), which accepts zero or one row. If the row should exist, check that the user is signed in and that a row level security policy lets them read it. If two rows match, filter on a unique column.
- Supabase query problemSupabase select returns an empty array, but the data is thereCheck that the table has a select policy: with RLS on and no policy, the API returns no data to the browser key. Then check who the request runs as: a signed-out user or an expired session matches no rows in a policy based on auth.uid(). If you get error 42501 instead of an empty array, the role is missing a grant.
Looking for another platform? See every fix.
A different Supabase error?
Describe it and get a written quote
Send the error and what you were doing. You get a written scope, a fixed price and a timeline; we reply by email, usually within one business day.