<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: VibeFix</title>
    <description>The latest articles on DEV Community by VibeFix (@vibefix).</description>
    <link>https://dev.to/vibefix</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4190639%2F9ff97927-38cc-46db-88ec-fc56f254b7d0.jpeg</url>
      <title>DEV Community: VibeFix</title>
      <link>https://dev.to/vibefix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vibefix"/>
    <language>en</language>
    <item>
      <title>Lovable login loop: users sign up, then land back on the login page (4 causes)</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Sun, 01 Nov 2026 09:46:25 +0000</pubDate>
      <link>https://dev.to/vibefix/lovable-login-loop-users-sign-up-then-land-back-on-the-login-page-4-causes-2ale</link>
      <guid>https://dev.to/vibefix/lovable-login-loop-users-sign-up-then-land-back-on-the-login-page-4-causes-2ale</guid>
      <description>&lt;p&gt;Someone signs up to your app, and instead of getting in they end up back on the sign-up or login page. Or they try to log in and get an error. They try again, it happens again, and they leave.&lt;/p&gt;

&lt;p&gt;You don't see it, because you're already signed in. Here are the usual reasons in apps built with AI tools, and how to fix each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, find out which one it is
&lt;/h2&gt;

&lt;p&gt;Open your &lt;strong&gt;live&lt;/strong&gt; site in a private window, ideally on your phone, and sign up with an email address you've never used on the app. Then note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Did a confirmation email arrive?&lt;/strong&gt; If not, see &lt;a href="https://vibe-fixer.com/guides/confirmation-emails-not-arriving" rel="noopener noreferrer"&gt;Sign-up email not arriving&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where did the link in that email take you?&lt;/strong&gt; If it opened &lt;code&gt;localhost&lt;/code&gt; or your preview, see &lt;a href="https://vibe-fixer.com/guides/confirmation-link-opens-localhost" rel="noopener noreferrer"&gt;Confirmation link opens localhost&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What does the screen say when you try to log in?&lt;/strong&gt; Write down the exact error text. It tells you which reason below applies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reason 1: the account isn't confirmed yet
&lt;/h2&gt;

&lt;p&gt;Most sign-in services make new people confirm their email before they can sign in. Supabase's docs say that on hosted projects "this is true by default". Lovable's docs say the same: with auto-confirm off, users "must verify the address before signing in".&lt;/p&gt;

&lt;p&gt;So if the confirmation email never arrives, or the person tries to log in before clicking it, the login is refused (Supabase's error code is &lt;code&gt;email_not_confirmed&lt;/code&gt;), and to them it looks like a loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; make the confirmation email reliable first; &lt;a href="https://vibe-fixer.com/guides/confirmation-emails-not-arriving" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; covers it. For someone already stuck, Lovable's docs note that "signing in again does not resend it". They get a new email by signing up again with the same address. On Lovable's built-in backend you can also add them under &lt;strong&gt;More → Users → Add user → Create new user&lt;/strong&gt;, and accounts added that way are confirmed automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 2: the live address isn't on the allowed list
&lt;/h2&gt;

&lt;p&gt;After sign-up or sign-in, the sign-in service sends people back to your app, but only to addresses on its allowed list. Otherwise it uses its default address, which for Supabase starts as &lt;code&gt;localhost:3000&lt;/code&gt;. Lovable's docs put it plainly: "If sign-in works in preview but breaks on your published app or custom domain, these URLs are usually the reason."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; (including Bolt and most AI tools that use it): open &lt;strong&gt;Authentication → URL Configuration&lt;/strong&gt;. Set &lt;strong&gt;Site URL&lt;/strong&gt; to your live address, and add every live address under &lt;strong&gt;Redirect URLs&lt;/strong&gt; (with and without &lt;code&gt;www&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lovable:&lt;/strong&gt; ask Lovable in the chat to update them for your domain, or add your address under &lt;strong&gt;Auth settings → Advanced → Redirect URLs&lt;/strong&gt;. If the app uses your own Supabase project, use the Supabase steps above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase:&lt;/strong&gt; if sign-in fails only on your live domain, check that the domain is on the list of authorized domains in Authentication settings, and add it if it's missing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reason 3: the app never finishes signing the person in
&lt;/h2&gt;

&lt;p&gt;Some apps (especially ones with server-side pages) use a sign-in flow where the email link brings back a one-time code, and the app has to swap that code for a signed-in session on the page it lands on. Supabase's docs say that code "can only be exchanged for an access token once", is valid for 5 minutes, and the swap "must be initiated on the same browser and device where the flow was started".&lt;/p&gt;

&lt;p&gt;So if the landing page doesn't do the swap, or the person opens the email link in a different browser (for example inside their email app), they arrive signed out and are sent back to login.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; paste this into your builder:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;New users sign up but end up back on the login page instead of signed in. Check the page the sign-up and sign-in links return to: it must turn the code in the link into a signed-in session, then send the person into the app. Also check that the session is kept after the next page load. Don't change anything else. Then tell me how to test it with a fresh email on the live site, opening the email link on the same phone.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 4: sign-up is switched off
&lt;/h2&gt;

&lt;p&gt;It sounds obvious, but it happens. Supabase has an &lt;strong&gt;Allow new users to sign up&lt;/strong&gt; setting, and Lovable has &lt;strong&gt;Disable sign-up&lt;/strong&gt;. With sign-up off, only existing users can get in, and Supabase answers new sign-ups with &lt;code&gt;signup_disabled&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; check that setting in your sign-in service and turn new sign-ups back on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still stuck in the loop?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=devto-login-loop" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When did you last sign up to your own app the way a stranger would, in a private window on your phone?&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/confirmation-emails-not-arriving" rel="noopener noreferrer"&gt;Sign-up email not arriving&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/confirmation-link-opens-localhost" rel="noopener noreferrer"&gt;Confirmation link opens localhost&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/google-sign-in-fails-on-live-site" rel="noopener noreferrer"&gt;Google sign-in fails on the live site&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lovable, Bolt, Replit, Base44, v0, Supabase and Firebase are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>webdev</category>
      <category>authentication</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Let Claude check your live app like a new user</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Thu, 29 Oct 2026 11:09:41 +0000</pubDate>
      <link>https://dev.to/vibefix/let-claude-cursor-or-codex-check-your-live-app-like-a-new-user-5h9j</link>
      <guid>https://dev.to/vibefix/let-claude-cursor-or-codex-check-your-live-app-like-a-new-user-5h9j</guid>
      <description>&lt;p&gt;Your app works for you. You're signed in, your customers aren't. The first screen a brand-new customer hits is the one you never see.&lt;/p&gt;

&lt;p&gt;VibeFix now works inside Claude. The same server can be added to Cursor and Codex too; their setup is below, but we haven't finished a test run in those two yet. When your app breaks, ask your AI tool to check the live app. It calls VibeFix, and our AI agent signs up like a new user and shows you, with a screenshot, whether your app breaks for them. It only checks your own app, and only when you say yes.&lt;/p&gt;

&lt;p&gt;Here is the setup for each tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code
&lt;/h2&gt;

&lt;p&gt;Connect the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;claude mcp add --transport http vibefix https://vibe-fixer.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install the server and the skill together as a plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude plugin marketplace add yos88da/vibefix-mcp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; claude plugin &lt;span class="nb"&gt;install &lt;/span&gt;vibefix@vibefix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Claude (desktop and web)
&lt;/h2&gt;

&lt;p&gt;Go to Customize, Connectors, press +, then Add custom connector. Paste &lt;code&gt;https://vibe-fixer.com/mcp&lt;/code&gt; and press Add. VibeFix is also listed in Claude's connector directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cursor
&lt;/h2&gt;

&lt;p&gt;Add the server to &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"vibefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://vibe-fixer.com/mcp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the project rule that tells Cursor when to call it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/yos88da/vibefix-mcp/main/agent-rules/cursor/vibefix.mdc &lt;span class="nt"&gt;--create-dirs&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; .cursor/rules/vibefix.mdc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Codex
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;codex mcp add vibefix &lt;span class="nt"&gt;--url&lt;/span&gt; https://vibe-fixer.com/mcp
codex mcp login vibefix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the AGENTS.md section that tells Codex when to call it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/yos88da/vibefix-mcp/main/agent-rules/AGENTS.md &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; AGENTS.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Any agent that takes skills
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add yos88da/vibefix-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What to ask
&lt;/h2&gt;

&lt;p&gt;"Check my live app at &lt;a href="https://example.com" rel="noopener noreferrer"&gt;https://example.com&lt;/a&gt; like a brand-new user."&lt;/p&gt;

&lt;p&gt;The skill also tells your agent to offer a check when you say your deployed app is broken, or right after you publish a change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/tools?src=devto-agent-launch" rel="noopener noreferrer"&gt;vibe-fixer.com/tools&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which tool do you build with, and would you let it test your live app the way a stranger would?&lt;/p&gt;

&lt;p&gt;Claude, Cursor and Codex are trademarks of their owners. VibeFix is independent and is not affiliated with or endorsed by them.&lt;/p&gt;

&lt;p&gt;I'm building VibeFix.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>webdev</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>"new row violates row-level security policy": why data won't save in Supabase</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Mon, 26 Oct 2026 09:00:44 +0000</pubDate>
      <link>https://dev.to/vibefix/new-row-violates-row-level-security-policy-why-data-wont-save-in-supabase-1e00</link>
      <guid>https://dev.to/vibefix/new-row-violates-row-level-security-policy-why-data-wont-save-in-supabase-1e00</guid>
      <description>&lt;p&gt;A user fills in a form, presses Save, and nothing happens. The console or logs show "new row violates row-level security policy for table", followed by the table name. A public example: &lt;code&gt;new row violates row-level security policy for table "players_secure"&lt;/code&gt; (&lt;a href="https://github.com/supabase/postgres/issues/1708" rel="noopener noreferrer"&gt;supabase/postgres #1708&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Row-level security (RLS) is a set of rules on each table about who may read or write which rows. Supabase says that once RLS is enabled, "no data is accessible through the API when using a publishable key, until you create policies." PostgreSQL says inserted rows that do not pass the policy "will result in a policy violation error, and the entire INSERT command will be aborted."&lt;/p&gt;

&lt;p&gt;So the database is protecting the table as designed. The fix is a policy that lets the right person write the right row, not switching RLS off. You can add policies in the Supabase SQL editor; replace the table and column names with yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. There is no INSERT policy on the table
&lt;/h2&gt;

&lt;p&gt;A table may have a policy for reading but none for adding rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; add an insert policy for signed-in users that checks the row belongs to them, adapted from Supabase's profiles example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Users can create their own rows"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then save again from the app while signed in.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;Supabase: Row Level Security&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The row does not carry the signed-in user's id
&lt;/h2&gt;

&lt;p&gt;The policy compares a column (such as &lt;code&gt;user_id&lt;/code&gt;) with the signed-in user. If the app leaves that column empty, or fills it with another id, the check fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; in the code that saves, set &lt;code&gt;user_id&lt;/code&gt; to the id of the signed-in user from the current session, and make sure the column name in the policy matches the column the app fills.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;Supabase: Row Level Security&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The person is not signed in when the save runs
&lt;/h2&gt;

&lt;p&gt;Supabase says that when a request has no authenticated user, &lt;code&gt;auth.uid()&lt;/code&gt; returns null, so a policy like &lt;code&gt;auth.uid() = user_id&lt;/code&gt; silently fails for them. It recommends &lt;code&gt;auth.uid() IS NOT NULL AND auth.uid() = user_id&lt;/code&gt;. This happens when a form is used before sign-in, or when the session has expired.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require sign-in before the screen that saves, or wait until the session exists.&lt;/li&gt;
&lt;li&gt;If the form is meant for visitors who are not signed in (a contact form, for example), write a separate insert policy for the &lt;code&gt;anon&lt;/code&gt; role that allows only that table and only the fields it needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;Supabase: Row Level Security&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The app asks for the new row back, and there is no SELECT policy
&lt;/h2&gt;

&lt;p&gt;PostgreSQL says that if an insert returns the new row, "any newly inserted or updated rows from the relation must satisfy the relation's SELECT policies", and otherwise an error is thrown. In supabase-js this is an insert followed by &lt;code&gt;.select()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; add a select policy so people can read their own rows, or stop asking for the row back if the app does not need it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Users can view their own rows"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sources: &lt;a href="https://www.postgresql.org/docs/current/sql-createpolicy.html" rel="noopener noreferrer"&gt;PostgreSQL: CREATE POLICY&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;Supabase: Row Level Security&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. It is an upsert or an update
&lt;/h2&gt;

&lt;p&gt;An upsert needs more than an insert policy. PostgreSQL checks the SELECT policies for an insert with ON CONFLICT DO UPDATE, and the UPDATE policy applies to its update part. Supabase also warns that without a SELECT policy, an UPDATE "will not work as expected."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; add an update policy for the owner of the row, with both &lt;code&gt;using&lt;/code&gt; and &lt;code&gt;with check&lt;/code&gt; comparing &lt;code&gt;(select auth.uid())&lt;/code&gt; with &lt;code&gt;user_id&lt;/code&gt;, and make sure a select policy exists too (cause 4).&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://www.postgresql.org/docs/current/sql-createpolicy.html" rel="noopener noreferrer"&gt;PostgreSQL: CREATE POLICY&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;Supabase: Row Level Security&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. It is a file upload to Supabase Storage
&lt;/h2&gt;

&lt;p&gt;Uploads are rows in the &lt;code&gt;storage.objects&lt;/code&gt; table. Supabase says "by default Storage does not allow any uploads to buckets without RLS policies", and that the only policy needed for uploading is INSERT on &lt;code&gt;storage.objects&lt;/code&gt;. Overwriting with upsert also needs SELECT and UPDATE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; add an insert policy on &lt;code&gt;storage.objects&lt;/code&gt; for the &lt;code&gt;authenticated&lt;/code&gt; role, limited to your bucket (Supabase's example checks &lt;code&gt;bucket_id&lt;/code&gt; and the folder with &lt;code&gt;storage.foldername(name)&lt;/code&gt;). If the app overwrites files, add select and update policies as well.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/storage/security/access-control" rel="noopener noreferrer"&gt;Supabase Storage: Access control&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you can't see it yourself
&lt;/h2&gt;

&lt;p&gt;You're signed in, your customers aren't. You test as the owner, often from the builder, signed in, with rows that already exist. A brand-new account, or a visitor who is not signed in, is governed by the rules you never tried.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can I just turn row-level security off?&lt;/strong&gt; The error disappears because the table becomes open to anyone with your public key, which is the opposite of what you want. Supabase also says never to use the secret or service_role key in the browser to get around it, because it bypasses RLS. Write the missing policy instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same insert works in the SQL editor but fails from the app. Why?&lt;/strong&gt; A public report (&lt;a href="https://github.com/supabase/postgres/issues/1708" rel="noopener noreferrer"&gt;supabase/postgres #1708&lt;/a&gt;) describes exactly that. The app sends requests as the &lt;code&gt;anon&lt;/code&gt; or &lt;code&gt;authenticated&lt;/code&gt; role, and policies are written for those roles, so test policies as the role your app uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixed it? Prove a real stranger gets in
&lt;/h2&gt;

&lt;p&gt;After the fix, the proof is a brand-new account saving its first row on your live app, and not seeing anyone else's. Your own account cannot show that, because it is not new. Your AI reads your code. VibeFix signs up like a stranger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=devto-rls-insert" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/users-can-see-other-users-data" rel="noopener noreferrer"&gt;Users can see other users' data&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/supabase-permission-denied-for-table-42501" rel="noopener noreferrer"&gt;Supabase "permission denied for table" (42501)&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/supabase-database-error-saving-new-user" rel="noopener noreferrer"&gt;"Database error saving new user" on sign-up&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Supabase is a trademark of its owner.&lt;/p&gt;

&lt;p&gt;Have you tried saving a row from a brand-new account on your live app, not your own?&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Lovable is a trademark of its owner.&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>security</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Lovable publish not working: what is failing, and what a visitor sees</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Sun, 25 Oct 2026 09:13:17 +0000</pubDate>
      <link>https://dev.to/vibefix/lovable-publish-not-working-what-is-failing-and-what-a-visitor-sees-146k</link>
      <guid>https://dev.to/vibefix/lovable-publish-not-working-what-is-failing-and-what-a-visitor-sees-146k</guid>
      <description>&lt;p&gt;Your app says it published, and a visitor still sees an old version, a locked page or a sign-in that fails. Or the Publish button itself stops with a "Publishing failed" banner. "Lovable publish not working" covers both, and they have different fixes.&lt;/p&gt;

&lt;p&gt;The preview and the published app are separate. Lovable says only publishing puts your app on a public URL, and later changes are not pushed live on their own. Hosting serves the pages; the backend handles data and sign-in. Either side can be the one failing.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the Publish button fails
&lt;/h2&gt;

&lt;p&gt;The banner names one of six categories and offers the matching action.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Temporary issue.&lt;/strong&gt; A short-lived problem such as a timeout or a rate limit on a connected service. Click Try again; Lovable says retries usually succeed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error in your app.&lt;/strong&gt; A build error or an edge function that could not be deployed: the app ran in the preview but cannot be built for production. Click Try to fix, or describe the issue in the project chat, then publish again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration issue.&lt;/strong&gt; A problem in the project setup, such as a file too large to include. Click Try to fix, then publish again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External connection or secret.&lt;/strong&gt; Something outside the project, such as an expired Supabase connection or a connected service at its plan limits. Reconnect the integration or update its plan or quota, then click Try again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database change conflicts with existing data.&lt;/strong&gt; Click Ask the assistant to help and go through the fix with it, since it touches real data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal error.&lt;/strong&gt; Lovable's banner puts this one on its own side. Try again after a few minutes; if it keeps failing, contact Lovable support with your project link.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Source: &lt;a href="https://docs.lovable.dev/features/publish" rel="noopener noreferrer"&gt;Lovable: Publish your project&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When publishing is blocked, not failing
&lt;/h2&gt;

&lt;p&gt;Some states stop publishing without a failure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Fix security issues to publish": the workspace blocks publishing with critical findings. Resolve them, then publish.&lt;/li&gt;
&lt;li&gt;"Publish requires approval" (an Enterprise workspace policy): select Request approval and write a short note for the approvers.&lt;/li&gt;
&lt;li&gt;"You don't have permission to publish this project": publishing needs editor access or above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://docs.lovable.dev/features/publish" rel="noopener noreferrer"&gt;Lovable: Publish your project&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When it published, but visitors still have a problem
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;They see the old version.&lt;/strong&gt; A dot on the Publish button means the project has changes newer than the live version. Open the Publish dialog and click Publish changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nobody else can open it.&lt;/strong&gt; On Free and Pro, visibility is Public by default with an Invite only option; Business and Enterprise have Public, Workspace and Custom. Set the audience to Public in the Publish dialog, then click Publish changes so it takes effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages load, but sign-in and data fail.&lt;/strong&gt; The backend may be paused. Lovable says pausing affects the app's data and logic, not page hosting, and a paused backend may not restart on its own. Go to More, Database and click Wake up on the paused card.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The custom domain is not Live.&lt;/strong&gt; Until it shows Live, visitors on that address do not reach the app. Open Project, Settings, Domains and read the status; if it shows Offline, click Recover, review the DNS records shown and update them at your DNS provider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is down for everyone.&lt;/strong&gt; Check status.lovable.dev for incidents, open the Publish dialog to confirm the project is still published, and check your email for an inactivity notice (Free-plan projects can be unpublished for inactivity). If visitors see "App temporarily unavailable", the Free-plan request limit was reached: newer apps are limited to 1,000 server requests per project per 10 seconds, and visitors can try again after a minute.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://docs.lovable.dev/features/hosting" rel="noopener noreferrer"&gt;Lovable: How Lovable hosts your app&lt;/a&gt; · &lt;a href="https://docs.lovable.dev/features/custom-domain" rel="noopener noreferrer"&gt;Lovable: Custom domains&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you can't see it yourself
&lt;/h2&gt;

&lt;p&gt;You're signed in, your customers aren't. In the editor you see the banner and the preview, signed in as an editor, with an awake backend. A visitor sees only the published address and meets whatever the published side actually does.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why can't visitors see my latest changes?&lt;/strong&gt; Lovable does not push later edits live automatically. A dot on the Publish button means there are newer changes; click Publish changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does a paused backend take my site down?&lt;/strong&gt; No. Pages still load, but data and sign-in fail until you click Wake up under More, Database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Published? Prove a real stranger gets in
&lt;/h2&gt;

&lt;p&gt;A published app that works from your own account proves nothing about a new visitor. Your AI reads your code. VibeFix signs up like a stranger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=devto-lovable-publish" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/works-in-preview-not-after-deploy" rel="noopener noreferrer"&gt;It works in the preview but breaks after I deploy&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/lovable-sign-in-works-in-preview-fails-when-published" rel="noopener noreferrer"&gt;Supabase login not working on my published Lovable app&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/blank-page-after-publish" rel="noopener noreferrer"&gt;Blank white page after publishing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Lovable and Supabase are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;Which one got you: the Publish banner, or a visitor seeing something you never saw in the editor?&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>lovable</category>
      <category>webdev</category>
      <category>deployment</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>AuthApiError: Email not confirmed in Supabase: why new users can't sign in</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:00:11 +0000</pubDate>
      <link>https://dev.to/vibefix/authapierror-email-not-confirmed-in-supabase-why-new-users-cant-sign-in-237p</link>
      <guid>https://dev.to/vibefix/authapierror-email-not-confirmed-in-supabase-why-new-users-cant-sign-in-237p</guid>
      <description>&lt;p&gt;A new person signs up, tries to sign in, and your app says "Email not confirmed". In code it comes back as an &lt;code&gt;AuthApiError&lt;/code&gt; with the code &lt;code&gt;email_not_confirmed&lt;/code&gt;; public reports show the raw response as &lt;code&gt;{"code":"email_not_confirmed","message":"Email not confirmed"}&lt;/code&gt; (&lt;a href="https://github.com/supabase/supabase/issues/29632" rel="noopener noreferrer"&gt;Supabase issue #29632&lt;/a&gt;). The account exists, but the person is locked out of it.&lt;/p&gt;

&lt;p&gt;Supabase's error reference describes it as: "Signing in is not allowed for this user as the email address is not confirmed." The project is doing what it was told. With Confirm email on, users "will need to confirm their email address before signing in for the first time", and on hosted Supabase projects it is on by default. On self-hosted projects and in local development it is off by default, which is why an app can behave differently on a laptop than live.&lt;/p&gt;

&lt;p&gt;So the real question is why this person is still unconfirmed. Supabase also advises identifying errors by &lt;code&gt;error.code&lt;/code&gt;, not by matching the message text. Here are six reasons, in the order to check them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The confirmation email was never sent or never arrived
&lt;/h2&gt;

&lt;p&gt;Supabase's default email service only delivers to addresses on your project's team, at 2 messages per hour, and "is not meant for production use".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open your project's Auth logs and look for an error at the time of the sign-up. Supabase names this as the first step for undelivered auth emails.&lt;/li&gt;
&lt;li&gt;If you are on the default email service, set up your own SMTP provider and enable custom SMTP in the Authentication settings. Supabase lists Resend, AWS SES, Postmark, Twilio SendGrid, ZeptoMail and Brevo as examples.&lt;/li&gt;
&lt;li&gt;Set up SPF, DKIM and DMARC for the sending domain. Supabase recommends this for deliverability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt; · &lt;a href="https://supabase.com/kb/troubleshooting/not-receiving-auth-emails-from-the-supabase-project-OFSNzw" rel="noopener noreferrer"&gt;Supabase troubleshooting: Not receiving Auth emails&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The email arrived, but the link was already used up
&lt;/h2&gt;

&lt;p&gt;Supabase documents that some email providers' security features prefetch links in incoming emails. The one-time link is consumed before the person clicks it; they see "Token has expired or is invalid" and stay unconfirmed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the Email Templates page in the dashboard and edit the Confirm signup template.&lt;/li&gt;
&lt;li&gt;Choose one of Supabase's two documented options: send a one-time code by putting &lt;code&gt;{{ .Token }}&lt;/code&gt; in the template and verify it with &lt;code&gt;supabase.auth.verifyOtp&lt;/code&gt;, or link to a page in your app with a button that opens the real confirmation link.&lt;/li&gt;
&lt;li&gt;Send the affected person a new confirmation email (see cause 4).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/auth/auth-email-templates" rel="noopener noreferrer"&gt;Supabase: Email templates&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The link sent them to localhost or a page that does not finish the job
&lt;/h2&gt;

&lt;p&gt;Supabase's Site URL defaults to &lt;code&gt;http://localhost:3000&lt;/code&gt;, and it is used whenever the app does not name an approved redirect. With the PKCE flow, Supabase's template sends the person to &lt;code&gt;{{ .SiteURL }}/auth/confirm?token_hash=...&lt;/code&gt;, so confirmation only completes if that page exists on your live app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open URL Configuration in the dashboard and change the Site URL from localhost to your live address.&lt;/li&gt;
&lt;li&gt;Add your live address to the Redirect URLs list, for example &lt;code&gt;https://yourapp.com/**&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the sign-up call, pass &lt;code&gt;options: { emailRedirectTo: 'https://yourapp.com/welcome' }&lt;/code&gt;. The address must be on the Redirect URLs list.&lt;/li&gt;
&lt;li&gt;If your template uses a token-hash link, make sure your live app has the page it points to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/guides/auth/redirect-urls" rel="noopener noreferrer"&gt;Supabase: Redirect URLs&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/auth/passwords" rel="noopener noreferrer"&gt;Supabase: Password-based auth&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. They tried to sign in again, expecting a new email
&lt;/h2&gt;

&lt;p&gt;Signing in does not send a new confirmation email. Someone who keeps trying only gets the same refusal. Lovable's documentation says it plainly: "signing in again does not resend it".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Catch &lt;code&gt;email_not_confirmed&lt;/code&gt; in your sign-in screen and show a "Resend confirmation email" button instead of a generic error.&lt;/li&gt;
&lt;li&gt;Have the button call &lt;code&gt;supabase.auth.resend({ type: 'signup', email, options: { emailRedirectTo } })&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Disable the button for at least 60 seconds after each send. Supabase's default allows one signup confirmation request per user per 60 seconds.&lt;/li&gt;
&lt;li&gt;On an app on Lovable's built-in backend without such a button, Lovable's documented route is for the person to sign up again with the same address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/reference/javascript/auth-resend" rel="noopener noreferrer"&gt;Supabase JavaScript reference: resend()&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/auth/rate-limits" rel="noopener noreferrer"&gt;Supabase: Rate limits&lt;/a&gt; · &lt;a href="https://docs.lovable.dev/features/email-auth" rel="noopener noreferrer"&gt;Lovable: Email authentication&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The account was created outside the normal sign-up
&lt;/h2&gt;

&lt;p&gt;Users created with the admin API (&lt;code&gt;auth.admin.createUser&lt;/code&gt;) without &lt;code&gt;email_confirm: true&lt;/code&gt; can be left unconfirmed, and then hit the same error. Public reports describe this even after Confirm email was switched off (&lt;a href="https://github.com/supabase/supabase/issues/29632" rel="noopener noreferrer"&gt;#29632&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm the account on the server with &lt;code&gt;supabase.auth.admin.updateUserById(id, { email_confirm: true })&lt;/code&gt;. Supabase says this function "should only be called on a server", and never with the service_role key in the browser.&lt;/li&gt;
&lt;li&gt;On Lovable's built-in backend, accounts you create under More, Users, Add user, Create new user are confirmed automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/reference/javascript/auth-admin-updateuserbyid" rel="noopener noreferrer"&gt;Supabase: auth.admin.updateUserById()&lt;/a&gt; · &lt;a href="https://docs.lovable.dev/features/email-auth" rel="noopener noreferrer"&gt;Lovable: Email authentication&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The app assumes people are signed in right after sign-up
&lt;/h2&gt;

&lt;p&gt;Confirmation is one switch for the whole project. If your screens send a new person straight into the app after sign-up, the next sign-in attempt meets this error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decide whether you want confirmation. Supabase keeps it on by default for hosted projects. In Lovable the matching setting is Auto-confirm email, and Lovable recommends keeping confirmation on for live apps.&lt;/li&gt;
&lt;li&gt;If confirmation stays on, show a "check your email" screen after sign-up instead of opening the app.&lt;/li&gt;
&lt;li&gt;Test the full path with a brand-new address, not your own account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/guides/auth/general-configuration" rel="noopener noreferrer"&gt;Supabase: General configuration&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/auth/passwords" rel="noopener noreferrer"&gt;Supabase: Password-based auth&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you can't see it yourself
&lt;/h2&gt;

&lt;p&gt;You're signed in, your customers aren't. You confirmed your own account long ago, from an address that is on the team. You never sit where a new person sits: signed up, unconfirmed, waiting for an email that may never come.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I switched off "Confirm email", but users still get "Email not confirmed". Why?&lt;/strong&gt; People report this on Supabase's GitHub, for example &lt;a href="https://github.com/supabase/supabase/issues/29632" rel="noopener noreferrer"&gt;#29632&lt;/a&gt; and &lt;a href="https://github.com/supabase/supabase/issues/5113" rel="noopener noreferrer"&gt;#5113&lt;/a&gt;. For an account that is already stuck, confirm that account: send a new confirmation email with &lt;code&gt;resend()&lt;/code&gt;, or confirm it on the server with &lt;code&gt;updateUserById&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can people use the app before confirming their email?&lt;/strong&gt; Only by switching confirmation off for the whole project. A request for a middle ground (&lt;a href="https://github.com/supabase/auth/issues/1494" rel="noopener noreferrer"&gt;supabase/auth #1494&lt;/a&gt;) was closed in 2024, with anonymous sign-in suggested as the alternative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixed it? Prove a real stranger gets in
&lt;/h2&gt;

&lt;p&gt;The test that matters is a brand-new address signing up, getting the email, clicking the link and signing in on your live app. Your own account cannot show that, because it is not new. Your AI reads your code. VibeFix signs up like a stranger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=devto-email-not-confirmed" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/supabase-error-sending-confirmation-email" rel="noopener noreferrer"&gt;Supabase "Error sending confirmation email" (500)&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/confirmation-emails-not-arriving" rel="noopener noreferrer"&gt;Supabase confirmation email not sending&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/supabase-redirect-url-localhost" rel="noopener noreferrer"&gt;Supabase redirect URL localhost&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Supabase and Lovable are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;Does your sign-in screen tell an unconfirmed user what to do next, or does it only show the error?&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>authentication</category>
      <category>javascript</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Supabase "Error sending confirmation email" (500): what it means and the 6 causes</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Fri, 23 Oct 2026 08:00:11 +0000</pubDate>
      <link>https://dev.to/vibefix/supabase-error-sending-confirmation-email-500-what-it-means-and-the-6-causes-cpe</link>
      <guid>https://dev.to/vibefix/supabase-error-sending-confirmation-email-500-what-it-means-and-the-6-causes-cpe</guid>
      <description>&lt;p&gt;A new person signs up and your app shows "Error sending confirmation email" instead of letting them in. Public reports show it as a 500 with the code &lt;code&gt;unexpected_failure&lt;/code&gt;: &lt;code&gt;{"code":"unexpected_failure","message":"Error sending confirmation email"}&lt;/code&gt; (&lt;a href="https://github.com/orgs/supabase/discussions/29703" rel="noopener noreferrer"&gt;Supabase discussion #29703&lt;/a&gt;; also &lt;a href="https://github.com/orgs/supabase/discussions/36181" rel="noopener noreferrer"&gt;#36181&lt;/a&gt;). Password reset can fail the same way with "Error sending recovery email".&lt;/p&gt;

&lt;p&gt;The 500 alone does not say why. The reason is written in one place, and most people never open it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the error means
&lt;/h2&gt;

&lt;p&gt;With Confirm email on, Supabase Auth has to hand a message to an email server before the sign-up can finish. This error means that hand-over failed. Supabase's error reference says a 500 indicates the Auth service is degraded and "most often it points to issues in your database setup", and &lt;code&gt;unexpected_failure&lt;/code&gt; means "Auth service is degraded or a bug is present, without a specific reason."&lt;/p&gt;

&lt;p&gt;The reason is in your project's Auth logs. In both public threads above, the first answer from a Supabase repository collaborator was the same: check the Auth logs for the message from the SMTP provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  0. Read the real error first
&lt;/h2&gt;

&lt;p&gt;Every cause below leaves a different line in the logs, so this decides which fix applies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the Supabase dashboard, open the Auth logs and find the entry at the time of the failed sign-up.&lt;/li&gt;
&lt;li&gt;If the line mentions your SMTP provider (authentication failed, sender not allowed, certificate, connection), go to cause 2 or 3.&lt;/li&gt;
&lt;li&gt;If it mentions a database object, trigger or relation, the problem is in the database ("Database error saving new user" is the usual sign).&lt;/li&gt;
&lt;li&gt;If you send email through a provider, check that provider's own logs next. Supabase names them as the next place to look.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/kb/troubleshooting/not-receiving-auth-emails-from-the-supabase-project-OFSNzw" rel="noopener noreferrer"&gt;Supabase troubleshooting: Not receiving Auth emails&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. You are on the default email service and the address is not on your team
&lt;/h2&gt;

&lt;p&gt;Unless you configure your own SMTP server, Supabase Auth "will refuse to deliver messages to addresses that are not part of the project's team". The error code reference lists &lt;code&gt;email_address_not_authorized&lt;/code&gt;: "Emails can only be sent to members in your Supabase organization." The default service also allows 2 messages per hour and is "not meant for production use". Every address you test with is on the team. Every stranger is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose an email provider. Supabase names Resend, AWS SES, Postmark, Twilio SendGrid, ZeptoMail and Brevo as examples.&lt;/li&gt;
&lt;li&gt;From the provider, copy the SMTP host, port, username and password, and decide the sender address and sender name.&lt;/li&gt;
&lt;li&gt;In the Supabase dashboard, open the Authentication settings, enable custom SMTP and enter those values.&lt;/li&gt;
&lt;li&gt;Sign up again with an address that is not on your team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Custom SMTP is on, but a setting is wrong
&lt;/h2&gt;

&lt;p&gt;Supabase says "handover issues can occur due to misconfiguration of the custom SMTP provider".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare each field in Supabase (host, port, username, password, sender address) with the values on your provider's SMTP page, character for character.&lt;/li&gt;
&lt;li&gt;Use the SMTP username and password the provider issued, not your login to the provider's website.&lt;/li&gt;
&lt;li&gt;Make sure the sender address is one your provider allows you to send from.&lt;/li&gt;
&lt;li&gt;Save, then sign up again with a new address and read the Auth logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The sender domain is not verified with your provider
&lt;/h2&gt;

&lt;p&gt;Email providers ask you to verify the domain you send from. In &lt;a href="https://github.com/orgs/supabase/discussions/29703" rel="noopener noreferrer"&gt;discussion #29703&lt;/a&gt;, the fix was adding the provider's DNS records: "adding the DNS for resend did the trick."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your email provider, add your sending domain and copy the DNS records it gives you.&lt;/li&gt;
&lt;li&gt;Add those records at your domain's DNS host and wait for the provider to show the domain as verified.&lt;/li&gt;
&lt;li&gt;Set up SPF, DKIM and DMARC for that domain, which Supabase recommends for deliverability.&lt;/li&gt;
&lt;li&gt;Use a sender address on that verified domain in the Supabase SMTP settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A Send Email hook does not answer
&lt;/h2&gt;

&lt;p&gt;If your project sends auth emails through a Send Email hook, Supabase lists &lt;code&gt;hook_timeout&lt;/code&gt;: "Unable to reach hook within maximum time allocated." Only projects that use a hook can have this cause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check that the hook's endpoint or function is deployed and reachable.&lt;/li&gt;
&lt;li&gt;Look at the hook's own logs for errors or slow responses at the time of the failure.&lt;/li&gt;
&lt;li&gt;If you do not need the hook, switch back to SMTP sending.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/auth/debugging/error-codes" rel="noopener noreferrer"&gt;Supabase: Auth error codes&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. A sending limit was reached
&lt;/h2&gt;

&lt;p&gt;When a limit is the cause, the message is usually different: "email rate limit exceeded", with status 429. With custom SMTP, Supabase starts you at 30 messages per hour; raise it on the Rate Limits page of the Authentication section once your own SMTP is set up.&lt;/p&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/guides/auth/rate-limits" rel="noopener noreferrer"&gt;Supabase: Rate limits&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Supabase Auth itself is degraded
&lt;/h2&gt;

&lt;p&gt;The one that is not yours to change. The Auth logs show no provider or database error. Check Supabase's system status page for an incident, retry after it is resolved, then sign up with a new address.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/auth/debugging/error-codes" rel="noopener noreferrer"&gt;Supabase: Auth error codes&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you can't see it yourself
&lt;/h2&gt;

&lt;p&gt;You're signed in, your customers aren't. Your own address is on the team, so the default sender accepts it. You sign up once, so you never hit an hourly limit. A custom provider can accept the address you tested and still refuse the next one. Only a brand-new address from outside the team meets these causes.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It worked yesterday. Why did it suddenly stop?&lt;/strong&gt; This exact question was asked in &lt;a href="https://github.com/orgs/supabase/discussions/36181" rel="noopener noreferrer"&gt;discussion #36181&lt;/a&gt;. The answer was to check the Auth log for an error from the SMTP provider. Read that line before changing anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did the sign-up still create the user?&lt;/strong&gt; Check the Users list in your Supabase dashboard. If the user exists but is unconfirmed, they will later meet "Email not confirmed". Once email works, send a new confirmation with &lt;a href="https://supabase.com/docs/reference/javascript/auth-resend" rel="noopener noreferrer"&gt;&lt;code&gt;resend()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I just turn off email confirmation?&lt;/strong&gt; Sign-ups will no longer send that email, but anyone can then register with an address they do not own, and password reset emails still need a working sender.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixed it? Prove a real stranger gets in
&lt;/h2&gt;

&lt;p&gt;After the fix, prove it with an address that is not on your team: it must get the email, confirm and sign in. Your own account cannot show that, because it is not new. Your AI reads your code. VibeFix signs up like a stranger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=devto-error-sending-email" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/confirmation-emails-not-arriving" rel="noopener noreferrer"&gt;Supabase confirmation email not sending&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/supabase-email-not-confirmed-error" rel="noopener noreferrer"&gt;Supabase "Email not confirmed" error&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/supabase-database-error-saving-new-user" rel="noopener noreferrer"&gt;"Database error saving new user"&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Supabase, Resend, AWS SES, Postmark, Twilio SendGrid, ZeptoMail and Brevo are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;When this error hit your app, what did the Auth log line actually say?&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Lovable is a trademark of its owner.&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>email</category>
      <category>authentication</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Supabase confirmation email not sending: why new users never get it</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Thu, 22 Oct 2026 18:49:38 +0000</pubDate>
      <link>https://dev.to/vibefix/supabase-confirmation-email-not-sending-why-new-users-never-get-it-506a</link>
      <guid>https://dev.to/vibefix/supabase-confirmation-email-not-sending-why-new-users-never-get-it-506a</guid>
      <description>&lt;p&gt;It worked every time you tried it. Then real people sign up, wait for the confirmation email, and nothing comes. Supabase's built-in sender only delivers to addresses on your project's team, sends 2 messages an hour, and is not meant for production. Your own address is on the team. A stranger's is not.&lt;/p&gt;

&lt;p&gt;Sign-up only finishes when the person clicks the link or enters the code in that email. Whatever sends your app's emails decides who gets them, how many go out per hour and whether they land in the inbox. The default senders are made for trying things out, not for strangers arriving from an ad or a post.&lt;/p&gt;

&lt;p&gt;Here are the seven causes, and the fix for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Supabase's default sender only delivers to your team
&lt;/h2&gt;

&lt;p&gt;Unless you set up your own SMTP server, Supabase only sends to addresses that are part of the project's team. Everyone else's message fails (the error code is &lt;code&gt;email_address_not_authorized&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From your email provider, get the SMTP host, port, user and password, and choose a From address on your own domain (for example &lt;code&gt;no-reply@yourdomain.com&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;In the Supabase dashboard, open the Authentication settings page and enable custom SMTP.&lt;/li&gt;
&lt;li&gt;Enter the host, port, user, password and From address, and save.&lt;/li&gt;
&lt;li&gt;Sign up with an address that is not on your team and confirm the email arrives.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The hourly email limit was reached
&lt;/h2&gt;

&lt;p&gt;The default service sends 2 messages per hour for the whole project. Once custom SMTP is saved, Supabase starts you at 30 messages per hour. A few sign-ups close together, or a launch day, use that up and later people get nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up custom SMTP first (cause 1). Supabase says the email limit can be configured once you use custom SMTP or a Send Email hook.&lt;/li&gt;
&lt;li&gt;Open Authentication, Rate Limits in the dashboard.&lt;/li&gt;
&lt;li&gt;Raise the email limit to fit the sign-ups you expect, within what your email provider allows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/auth/rate-limits" rel="noopener noreferrer"&gt;Supabase: Rate limits&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Your own sender is connected, but the hand-over fails or mail lands in spam
&lt;/h2&gt;

&lt;p&gt;Supabase hands the message to your provider. A wrong setting stops that hand-over; after it, Supabase says it "has no control over email delivery". Providers keep suppression lists of addresses that bounced before, and recipients' mail filters can quarantine or spam the message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check your project's Auth logs first; a failed hand-over shows there.&lt;/li&gt;
&lt;li&gt;Then check your email provider's own logs for blocked or suppressed addresses.&lt;/li&gt;
&lt;li&gt;With your provider, set up DKIM, DMARC and SPF for the sending domain.&lt;/li&gt;
&lt;li&gt;Send from your own domain, and keep sign-in emails separate from marketing emails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/kb/troubleshooting/not-receiving-auth-emails-from-the-supabase-project-OFSNzw" rel="noopener noreferrer"&gt;Supabase troubleshooting: Not receiving Auth emails&lt;/a&gt; · &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;Supabase: Send emails with custom SMTP&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Lovable: the project's hourly email cap
&lt;/h2&gt;

&lt;p&gt;Apps on Lovable's built-in backend have a per-project "Rate limit for sending emails" that caps sign-in emails per hour; Lovable says it is the first thing to check when confirmations stop arriving after many sign-ups. Lovable's Emails feature also limits authentication emails per workspace per hour (Pro 500, Business 3,000, Enterprise 10,000), and at a workspace limit new emails are rejected until the hour resets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open More, Users, Settings, Email, and look under Advanced for "Rate limit for sending emails".&lt;/li&gt;
&lt;li&gt;Raise it to match your expected sign-ups. Lovable notes that raising the sending limit needs managed email or a verified custom domain first.&lt;/li&gt;
&lt;li&gt;Open More, Emails to see the workspace usage; Lovable shows a warning at 80% of a limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://docs.lovable.dev/features/email-auth" rel="noopener noreferrer"&gt;Lovable: Email authentication for your app&lt;/a&gt; · &lt;a href="https://docs.lovable.dev/features/custom-emails" rel="noopener noreferrer"&gt;Lovable: Send branded emails from your own domain&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Lovable: emails from a new domain land in spam
&lt;/h2&gt;

&lt;p&gt;Lovable says emails from new domains often land in spam. On a paid workspace you can send sign-in emails from your own domain, with Lovable handling the DNS, SPF, DKIM and DMARC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask people who are waiting to check their spam folder first.&lt;/li&gt;
&lt;li&gt;Open More, Emails and click Get started (a paid workspace, a domain you own and access to its DNS are required).&lt;/li&gt;
&lt;li&gt;Enter your domain, keep or change the sender subdomain, then click Set up domain.&lt;/li&gt;
&lt;li&gt;Add the records Lovable shows. Add the NS records inside your DNS zone for the subdomain host shown; do not replace your domain's nameservers at your registrar.&lt;/li&gt;
&lt;li&gt;Wait for the status to show Verified (usually a few hours, up to 72), then send a test sign-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://docs.lovable.dev/features/custom-emails" rel="noopener noreferrer"&gt;Lovable: Send branded emails from your own domain&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Base44: the 6-digit code never arrives
&lt;/h2&gt;

&lt;p&gt;Base44 sends a 6-digit code that works for 10 minutes, from &lt;code&gt;no-reply@base44-apps.com&lt;/code&gt; unless you set up your own email domain. Email verification cannot be turned off, so a person without the code cannot log in with their password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask the person to check spam or junk, wait a few minutes, add &lt;code&gt;no-reply@base44-apps.com&lt;/code&gt; to their safe senders and check the address for typos.&lt;/li&gt;
&lt;li&gt;They can ask for a new code from the verification screen, or sign up again with the same address.&lt;/li&gt;
&lt;li&gt;If it still fails, remove their record from the Users list and ask them to sign up again. If several people report it at once, contact Base44 Customer Care with your app URL and the affected addresses.&lt;/li&gt;
&lt;li&gt;To send from your own domain (Starter plan or above, with a domain connected): Dashboard, Domains, Email domain, Use Your Custom Domain. Add the 3 CNAME records Base44 shows and wait for the status to be Active (up to 48 hours).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://docs.base44.com/Setting-up-your-app/Managing-login-and-registration" rel="noopener noreferrer"&gt;Base44: Managing login and registration&lt;/a&gt; · &lt;a href="https://docs.base44.com/Setting-up-your-app/Sending-emails-from-a-custom-domain" rel="noopener noreferrer"&gt;Base44: Sending emails from a custom domain&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The person missed the first email and cannot get another
&lt;/h2&gt;

&lt;p&gt;A person who deleted the email or typed a wrong address is left with an unconfirmed account. In Lovable, signing in again does not resend the email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supabase: give the app a "resend email" button that calls &lt;code&gt;supabase.auth.resend&lt;/code&gt; with type &lt;code&gt;signup&lt;/code&gt; and the person's address.&lt;/li&gt;
&lt;li&gt;Lovable: the person signs up again with the same address. You can also add them yourself under More, Users, Add user, Create new user; users added this way are confirmed automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sources: &lt;a href="https://supabase.com/docs/reference/javascript/auth-resend" rel="noopener noreferrer"&gt;Supabase JavaScript reference: resend()&lt;/a&gt; · &lt;a href="https://docs.lovable.dev/features/email-auth" rel="noopener noreferrer"&gt;Lovable: Email authentication for your app&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why you can't see it yourself
&lt;/h2&gt;

&lt;p&gt;You're signed in, your customers aren't. Your own address is on the team, so Supabase's default sender accepts it. You sign up once, so you never reach an hourly limit. Your mail provider knows you. A new customer is outside every one of those.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How many emails can Supabase send per hour?&lt;/strong&gt; The default service sends 2 per hour. After you set up custom SMTP, Supabase starts at 30 per hour, which you can change on the Rate Limits page. Your email provider may have its own limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I turn off email confirmation?&lt;/strong&gt; In Supabase, Confirm email is a setting, and Supabase says it is on by default for hosted projects. Lovable has an Auto-confirm email switch. Base44 does not let you turn verification off. Without confirmation, people get in without proving the address is theirs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixed it? Prove a real stranger gets in
&lt;/h2&gt;

&lt;p&gt;A fix that works from your own account proves nothing about a new customer. Your AI reads your code. VibeFix signs up like a stranger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=devto-signup-email" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/supabase-error-sending-confirmation-email" rel="noopener noreferrer"&gt;Supabase "Error sending confirmation email" (500)&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/supabase-email-not-confirmed-error" rel="noopener noreferrer"&gt;Supabase "Email not confirmed" error&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/confirmation-link-opens-localhost" rel="noopener noreferrer"&gt;Confirmation link opens localhost&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Supabase, Lovable, Bolt and Base44 are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;Have you signed up to your own live app with an address that isn't on your team, and did the email arrive?&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>authentication</category>
      <category>vibecoding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Sign in with Google fails on my live site: redirect_uri_mismatch</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Tue, 20 Oct 2026 08:01:07 +0000</pubDate>
      <link>https://dev.to/vibefix/sign-in-with-google-fails-on-my-live-site-redirecturimismatch-2j37</link>
      <guid>https://dev.to/vibefix/sign-in-with-google-fails-on-my-live-site-redirecturimismatch-2j37</guid>
      <description>&lt;p&gt;It works for you. But a new person taps &lt;strong&gt;Sign in with Google&lt;/strong&gt; on your live site and gets a Google error page, or lands back on your login page. Google's own error page tells them "only the app developer can fix this issue", so they leave.&lt;/p&gt;

&lt;p&gt;The cause is almost always a setting, not code. Here's how to find which one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the error Google shows
&lt;/h2&gt;

&lt;p&gt;Open your &lt;strong&gt;live&lt;/strong&gt; site in a private window, signed out of Google, and tap &lt;strong&gt;Sign in with Google&lt;/strong&gt;. Google's error page names the problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;400 redirect_uri_mismatch:&lt;/strong&gt; the return address isn't on your Google client's list (fix 1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;400 origin_mismatch:&lt;/strong&gt; your live site's address isn't on the list of allowed origins (fix 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;403 access_denied:&lt;/strong&gt; Google says this can mean "the app is in test mode and the developer hasn't added you as a test user" (fix 3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Google error, but you land back on your login page:&lt;/strong&gt; the sign-in service sent them to the wrong address afterwards (fix 4).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fix 1: the return address Google allows
&lt;/h2&gt;

&lt;p&gt;Google only returns people to addresses on your OAuth client's &lt;strong&gt;Authorized redirect URIs&lt;/strong&gt; list, and Google's docs say the address must match exactly: "the http or https scheme, case, and trailing slash ('/') must all match".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; (including Bolt and most AI tools that use it): in Supabase, open &lt;strong&gt;Authentication → Providers → Google&lt;/strong&gt; and copy the callback URL shown there. In Google Cloud, open your &lt;strong&gt;Web application&lt;/strong&gt; client and paste it under &lt;strong&gt;Authorized redirect URIs&lt;/strong&gt;. Note that this is Supabase's address, not your app's. If your Supabase project uses a custom domain, add that domain's callback too, "in addition to the Supabase project URL".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lovable, Managed by Lovable:&lt;/strong&gt; nothing to set. Lovable's docs say managed mode "needs no change when you connect a domain".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lovable, your own Google credentials:&lt;/strong&gt; when you connect a custom domain, Lovable adds its redirect URL to the list "but does not select it", and "until you do, Google sign-in fails on the new domain with a redirect URI mismatch error". Add that URL in Google Cloud, then select it in &lt;strong&gt;More → Cloud → Users → Auth settings → Google&lt;/strong&gt; and save.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase with a custom domain:&lt;/strong&gt; add &lt;code&gt;https://&amp;lt;your-domain&amp;gt;/__/auth/handler&lt;/code&gt; to the redirect URIs. Firebase's docs say "the trailing /__/auth/handler is important".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google notes that changes "may take 5 minutes to a few hours" to take effect, so wait before testing again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2: your live site as an allowed origin
&lt;/h2&gt;

&lt;p&gt;On the same Google client, add your live address under &lt;strong&gt;Authorized JavaScript origins&lt;/strong&gt;, just the address with no path (for example &lt;code&gt;https://myapp.com&lt;/code&gt;, not &lt;code&gt;https://myapp.com/login&lt;/code&gt;). Add both &lt;code&gt;www&lt;/code&gt; and non-&lt;code&gt;www&lt;/code&gt; if people use both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 3: the app is still in "Testing"
&lt;/h2&gt;

&lt;p&gt;In Google Cloud's sign-in settings, the app's publishing status can be &lt;strong&gt;Testing&lt;/strong&gt; or &lt;strong&gt;In production&lt;/strong&gt;. In Testing, Google limits access to listed test users, with one exception from Google's docs: if the app asks only for name, email and profile, which is plain Sign in with Google, people "do not need to be in the trusted user list".&lt;/p&gt;

&lt;p&gt;So if new people get &lt;strong&gt;403 access_denied&lt;/strong&gt;, check two things: which permissions your app asks Google for (anything beyond name, email and profile removes the exception), and the publishing status. Moving to &lt;strong&gt;In production&lt;/strong&gt; with &lt;strong&gt;Publish app&lt;/strong&gt; opens it to everyone. Google says apps using only the basic permissions don't need its verification review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 4: where people land after Google
&lt;/h2&gt;

&lt;p&gt;After Google, your sign-in service sends people back to your app, but only to addresses on its allowed list. In &lt;strong&gt;Supabase&lt;/strong&gt;, open &lt;strong&gt;Authentication → URL Configuration&lt;/strong&gt;, set &lt;strong&gt;Site URL&lt;/strong&gt; to your live address and add every live address under &lt;strong&gt;Redirect URLs&lt;/strong&gt;. On &lt;strong&gt;Lovable&lt;/strong&gt;, use &lt;strong&gt;Auth settings → Advanced → Redirect URLs&lt;/strong&gt; or ask Lovable in the chat. On &lt;strong&gt;Firebase&lt;/strong&gt;, add your live domain under &lt;strong&gt;Authentication → Settings → Authorized Domains&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Google Workspace accounts fail
&lt;/h2&gt;

&lt;p&gt;If only some people fail, and the error mentions "Error getting user email from external provider", Supabase's docs say some Google Workspace setups need the email permission requested explicitly. Ask your builder to request &lt;code&gt;openid&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;profile&lt;/code&gt; when starting Google sign-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check that new people can really sign up
&lt;/h2&gt;

&lt;p&gt;For Google sign-in itself, the 2-minute test above is the quickest check: a private window, signed out of Google, on your live site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=guide-google-signin" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/signup-loops-back-to-login" rel="noopener noreferrer"&gt;New users land back on the login page&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/confirmation-link-opens-localhost" rel="noopener noreferrer"&gt;Confirmation link opens localhost&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/confirmation-emails-not-arriving" rel="noopener noreferrer"&gt;Sign-up email not arriving&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lovable, Bolt, Replit, Base44, v0 and Supabase are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>webdev</category>
      <category>authentication</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Login broken after publishing: users land back on the login page</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:00:28 +0000</pubDate>
      <link>https://dev.to/vibefix/login-broken-after-publishing-users-land-back-on-the-login-page-5c1c</link>
      <guid>https://dev.to/vibefix/login-broken-after-publishing-users-land-back-on-the-login-page-5c1c</guid>
      <description>&lt;p&gt;Login worked in the preview. On the live site, users sign in and end up back on the login page, on a &lt;code&gt;localhost&lt;/code&gt; address, or on an error. Or the confirmation link in the email opens the wrong site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it happens
&lt;/h2&gt;

&lt;p&gt;When someone signs in, your login service (usually Supabase) sends them back to your app. For safety, it only sends people to addresses on an &lt;strong&gt;approved list&lt;/strong&gt;. Your preview address is on that list. Your published address often isn't, so the service falls back to its default, which is often &lt;code&gt;localhost&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;After signing in, look at the address bar. If it says &lt;code&gt;localhost:3000&lt;/code&gt; or your preview domain, this is the cause.&lt;/li&gt;
&lt;li&gt;Open the confirmation email a new user receives and check where the link points.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fix it (Supabase)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open your Supabase project → &lt;strong&gt;Authentication&lt;/strong&gt; → &lt;strong&gt;URL Configuration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Site URL&lt;/strong&gt; to your live address, e.g. &lt;code&gt;https://myapp.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Redirect URLs&lt;/strong&gt;, add your live address and, if you use more than one, each of them (with and without &lt;code&gt;www&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;If you use &lt;strong&gt;Google sign-in&lt;/strong&gt;, check the Google Cloud console lists the callback URL that Supabase shows you.&lt;/li&gt;
&lt;li&gt;Sign up with a fresh email on the live site and go through the whole flow once.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  If it still fails
&lt;/h2&gt;

&lt;p&gt;Some apps hard-code the preview address in the sign-in code. Ask your builder: &lt;em&gt;"Find every place the code builds a redirect or callback URL and make it use the live site's address."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sign-up is the one step every customer must pass. ## Still broken?&lt;/p&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=devto" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lovable, Bolt, Replit, Base44, v0 and Supabase are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Confirmation email link opens localhost instead of my app</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Sun, 18 Oct 2026 13:00:45 +0000</pubDate>
      <link>https://dev.to/vibefix/confirmation-email-link-opens-localhost-instead-of-my-app-4673</link>
      <guid>https://dev.to/vibefix/confirmation-email-link-opens-localhost-instead-of-my-app-4673</guid>
      <description>&lt;p&gt;A new person signs up, opens the confirmation email, taps the link, and gets "This site can't be reached", a &lt;code&gt;localhost&lt;/code&gt; address, or your preview instead of your app. Most of them stop right there. You never see it, because you're already signed in.&lt;/p&gt;

&lt;p&gt;It's one of the most common sign-up breaks in apps built with AI tools, and the fix is usually a setting, not code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it happens
&lt;/h2&gt;

&lt;p&gt;Your sign-in service decides where the email link sends people. It only sends them to addresses on an approved list, and when your app doesn't name one, it uses a single default address, the &lt;strong&gt;Site URL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That default often still points to a developer's computer. Supabase's own guide says to change the Site URL "from &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to your production URL", and calls the setting "critical for email confirmations and password resets". Bolt's help center says "By default, Bolt projects use localhost:3000 as the Site URL. This does not work for live applications", and that if the address isn't on the approved list, "Supabase falls back to the Site URL."&lt;/p&gt;

&lt;p&gt;So you publish, the app moves to its real address, and the email link still points to the old one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check it in 2 minutes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open your &lt;strong&gt;live&lt;/strong&gt; site in a private window, ideally on your phone.&lt;/li&gt;
&lt;li&gt;Sign up with an email address you've never used on the app.&lt;/li&gt;
&lt;li&gt;In the email, long-press the link (on a computer, hover over it) and read the address &lt;strong&gt;before&lt;/strong&gt; you tap it.&lt;/li&gt;
&lt;li&gt;If it starts with &lt;code&gt;http://localhost&lt;/code&gt;, &lt;code&gt;127.0.0.1&lt;/code&gt; or your preview address, this is the cause.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fix it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lovable, built-in backend (Cloud).&lt;/strong&gt; Lovable sets the Site URL to your published &lt;code&gt;lovable.app&lt;/code&gt; address when you publish, and adds a custom domain to the redirect URLs when you connect it. If the link is still wrong, ask Lovable in the chat to update them for your domain, or add your address under &lt;strong&gt;Auth settings → Advanced → Redirect URLs&lt;/strong&gt;. An app that uses your own Supabase project gets no automatic updates, so use the Supabase steps below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supabase (including Bolt and most other AI tools that use it).&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In your Supabase project, open &lt;strong&gt;Authentication&lt;/strong&gt;, then &lt;strong&gt;URL Configuration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Site URL&lt;/strong&gt; to your live address, for example &lt;code&gt;https://myapp.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Redirect URLs&lt;/strong&gt;, add each live address: your custom domain with and without &lt;code&gt;www&lt;/code&gt;, and the builder's published address if people use it.&lt;/li&gt;
&lt;li&gt;Save, then sign up again with a fresh email and check the link.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;If the setting is right and the link is still wrong&lt;/strong&gt;, the address is written into the app's code. Paste this into your builder:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;New users' confirmation emails link to localhost or the preview instead of my live site. Find every place the code sets a redirect or email redirect address for sign-up, sign-in and password reset, and make it use my live address: [your address]. Don't change anything else. Then tell me how to check it by signing up with a fresh email on the live site.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then publish and check the link in a new sign-up email.&lt;/p&gt;

&lt;h2&gt;
  
  
  People who signed up while it was broken
&lt;/h2&gt;

&lt;p&gt;Ask them to sign in on your live site. If the app says their email isn't confirmed, have them request a new confirmation email, or sign up again with the same address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;App broke after you published it? Our AI agent fixes it in your code, then tests your live app again so you see the result yourself. The first check is free. &lt;a href="https://vibe-fixer.com/?src=guide-localhost-link" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Related: &lt;a href="https://vibe-fixer.com/guides/confirmation-emails-not-arriving" rel="noopener noreferrer"&gt;Sign-up email not arriving&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/login-broken-after-publish" rel="noopener noreferrer"&gt;Sign-up or login broken after publishing&lt;/a&gt; · &lt;a href="https://vibe-fixer.com/guides/test-your-live-app-like-a-real-customer" rel="noopener noreferrer"&gt;Test your live app like a real customer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lovable, Bolt, Replit, Base44, v0 and Supabase are trademarks of their owners.&lt;/p&gt;

&lt;p&gt;Originally published at vibe-fixer.com. I'm building VibeFix, an AI agent that finds what is breaking your live app, fixes it in your code and re-checks it.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Supabase "permission denied for table" (42501) on new tables after 30 Oct 2026: the safe fix</title>
      <dc:creator>VibeFix</dc:creator>
      <pubDate>Sun, 18 Oct 2026 06:19:56 +0000</pubDate>
      <link>https://dev.to/vibefix/supabase-permission-denied-for-table-42501-after-30-oct-2026-the-fix-3jje</link>
      <guid>https://dev.to/vibefix/supabase-permission-denied-for-table-42501-after-30-oct-2026-the-fix-3jje</guid>
      <description>&lt;p&gt;Your newest Supabase table returns 42501 "permission denied for table", while every older table still works. With that message on a new table, the roles your app uses through the API have no GRANT on it. Supabase no longer gives the API access to new tables in the public schema automatically, and applies this to all existing projects from 30 October 2026. Add the GRANT and turn on row level security with policies in the same step: a GRANT with row level security off lets anyone who has your publishable (anon) key read every row, and that key is in your app's front end.&lt;/p&gt;

&lt;p&gt;A save fails, a list won't load or sign-up breaks, and the response says code 42501, "permission denied for table".&lt;/p&gt;

&lt;h2&gt;
  
  
  The exact error
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"42501"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission denied for table your_table"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Grant the required privileges to the current role with: GRANT SELECT ON public.your_table TO anon;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why it happens
&lt;/h2&gt;

&lt;p&gt;Supabase announced on 28 April 2026 that new tables in the public schema are not reachable through the API until access is granted. It started rolling this out as the default for new projects on 30 May 2026, and applies it to all existing projects from 30 October 2026. Tables you already have keep their current access. Existing tables are not affected; the change applies to tables created after it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible causes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A new table was created without granting access to the roles the API uses.&lt;/li&gt;
&lt;li&gt;Access was granted to one role (for example signed-out visitors) but not the one the app uses (signed-in users), or the other way round.&lt;/li&gt;
&lt;li&gt;It is a row-level security problem instead: Supabase also returns 42501 when row level security blocks an insert or update. Then the fix is a policy, not a grant.&lt;/li&gt;
&lt;li&gt;A table with an auto-numbered column is missing access to its sequence.&lt;/li&gt;
&lt;li&gt;An AI builder or script created the table and did not include the access it needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the hint is not the whole fix
&lt;/h2&gt;

&lt;p&gt;The hint names the missing GRANT. It does not check row level security. Grants decide whether a role can reach a table; row level security decides which rows. If row level security is off and you run &lt;code&gt;grant select ... to anon&lt;/code&gt;, every row can be read with the publishable (anon) key, and that key ships in your app's front end.&lt;/p&gt;

&lt;p&gt;Supabase's advice is to treat three steps as one unit: grant the privileges, enable row level security, add the policies you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix steps
&lt;/h2&gt;

&lt;p&gt;Run this SQL on your project's database. Replace &lt;code&gt;your_table&lt;/code&gt; and &lt;code&gt;user_id&lt;/code&gt; with your table and its owner column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Give the API roles access to the table&lt;/span&gt;
&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;service_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Only if signed-out visitors really need to read it:&lt;/span&gt;
&lt;span class="c1"&gt;-- grant select on public.your_table to anon;&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Turn on row level security&lt;/span&gt;
&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Let each signed-in user see and change only their own rows&lt;/span&gt;
&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"own rows: read"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"own rows: insert"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"own rows: update"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"own rows: delete"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the table has an auto-numbered id, grant its sequence too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;all&lt;/span&gt; &lt;span class="n"&gt;sequences&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;schema&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;service_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use migrations, put these statements in the same migration that creates the table, so the next new table does not break the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check which roles can reach the table
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;grantee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;privilege_type&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role_table_grants&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;table_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your_table'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that row level security is on for every public table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relrowsecurity&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;rls_on&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;relnamespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regnamespace&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;relkind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'r'&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any row with &lt;code&gt;rls_on = false&lt;/code&gt; is a table to fix before you add grants to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check that the data is hidden while signed out
&lt;/h2&gt;

&lt;p&gt;Run this on a table that has rows in it, using only the publishable (anon) key, the same one your app's front end uses. No sign-in token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://YOUR-PROJECT.supabase.co/rest/v1/your_table?select=*&amp;amp;limit=5"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"apikey: YOUR_PUBLISHABLE_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[]&lt;/code&gt; (empty list) on a table that has rows: signed-out visitors see nothing. This is what you want for private data.&lt;/li&gt;
&lt;li&gt;Rows come back: anyone with the public key can read them. If that is not intended, check &lt;code&gt;rls_on&lt;/code&gt; with the query above and the table's policies before anything else.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;42501 permission denied&lt;/code&gt;: signed-out visitors have no grant on this table. Fine if they should not read it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then sign in as one test user in your app and confirm you see your own rows and not another user's.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it can look fine at first
&lt;/h2&gt;

&lt;p&gt;Tables you made earlier keep their access, so the pages you use most keep working. A table added after the change, for example one your AI tool just created, fails until it gets its grant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still broken?
&lt;/h2&gt;

&lt;p&gt;If the error is gone but your live app still breaks for new users, start with a free check: &lt;a href="https://vibe-fixer.com/?src=devto-42501" rel="noopener noreferrer"&gt;Check my app free&lt;/a&gt;. Our AI agent signs up like a new user and shows you, with a screenshot, whether your app breaks for them. If you want it fixed, it fixes it in your code and tests your live app again.&lt;/p&gt;

&lt;p&gt;VibeFix is independent. Supabase is a trademark of its owner.&lt;/p&gt;

&lt;p&gt;When did you last request your newest table signed out, with only the public key?&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://vibe-fixer.com/guides/supabase-permission-denied-for-table-42501" rel="noopener noreferrer"&gt;vibe-fixer.com&lt;/a&gt;. I'm building VibeFix: our AI agent fixes what breaks in your live app in your code, then tests your live app again so you see the result yourself.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>security</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
