<?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: BuiltSafely</title>
    <description>The latest articles on DEV Community by BuiltSafely (builtsafely-org).</description>
    <link>https://dev.to/builtsafely-org</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%2Forganization%2Fprofile_image%2F15615%2Fffd7c846-b603-474a-9cfe-43eb518d13be.png</url>
      <title>DEV Community: BuiltSafely</title>
      <link>https://dev.to/builtsafely-org</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/builtsafely-org"/>
    <language>en</language>
    <item>
      <title>42501 permission denied for table</title>
      <dc:creator>BuiltSafely</dc:creator>
      <pubDate>Mon, 26 Oct 2026 14:22:00 +0000</pubDate>
      <link>https://dev.to/builtsafely-org/42501-permission-denied-for-table-2hhg</link>
      <guid>https://dev.to/builtsafely-org/42501-permission-denied-for-table-2hhg</guid>
      <description>&lt;p&gt;A 42501 "permission denied for table" error means the database role making the request was never granted access to that table, so Postgres stops before your row-level security policies even run. It often shows up after adding a table or running a migration, and some clients report it as a plain 401 or 403. The fix is a precise grant, not turning security off; here is how to find the missing privilege and add it safely.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI disclosure:&lt;/strong&gt; This article was written with AI. An AI agent adapted it from our original guide, and every claim links to the official source it came from. Steps marked &lt;strong&gt;Verified&lt;/strong&gt; were run by us in a throwaway test environment; steps marked &lt;strong&gt;Docs-only&lt;/strong&gt; come from the official docs and were not run end to end.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Last checked October 19, 2026 · Educational, not an audit.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Verified:&lt;/strong&gt; we ran this against a throwaway local Supabase Postgres 17 container (no real project). Details under &lt;em&gt;What we tested&lt;/em&gt; below.&lt;/p&gt;

&lt;p&gt;Grant the role the privilege it's missing, in a migration, and keep RLS on. The error's hint tells you which role and privilege it needs:&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;-- Signed-in users manage their own notes; signed-out visitors get nothing&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;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;notes&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;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;notes&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;-- ...plus one policy per operation (see our RLS guide)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;notes&lt;/code&gt; with your table. Grant only what each role needs. Only grant to &lt;code&gt;anon&lt;/code&gt; for data that's meant to be public.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we tested (October 16, 2026):&lt;/strong&gt; without a grant, a signed-out read failed with &lt;code&gt;42501: permission denied for table notes&lt;/code&gt; and the hint "Grant the required privileges to the current role with: GRANT SELECT ON public.notes TO anon;". A public table with &lt;code&gt;grant select ... to anon&lt;/code&gt;, RLS on and a read-only policy returned its rows to &lt;code&gt;anon&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Docs-only, except where marked&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Postgres checks grants before policies. Supabase's RLS guide says a missing grant "raises a &lt;code&gt;42501&lt;/code&gt; error before any policy runs", so check grants before you change policies (&lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;Supabase: RLS&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To see a table's grants (&lt;a href="https://supabase.com/docs/guides/troubleshooting/database-api-42501-errors" rel="noopener noreferrer"&gt;Supabase: 42501 errors&lt;/a&gt;):&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;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="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'notes'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verified:&lt;/strong&gt; on the same test container, this listed the roles and privileges for the table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why you may see it more on tables created on or after October 30, 2026:&lt;/strong&gt; Supabase applies a changed default to all existing projects on that date. &lt;strong&gt;Existing tables keep their current grants and stay reachable.&lt;/strong&gt; Only tables created in &lt;code&gt;public&lt;/code&gt; after the change need an explicit grant before the Data API can see them. Direct Postgres connections are unaffected (&lt;a href="https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically" rel="noopener noreferrer"&gt;Supabase changelog&lt;/a&gt;). Our &lt;a href="https://builtsafely.com/guides/supabase-oct30-grants/" rel="noopener noreferrer"&gt;Oct 30 grants guide&lt;/a&gt; covers the migration pattern.&lt;/p&gt;

&lt;p&gt;Clients often surface 42501 as a 401 or 403, so read the response body for &lt;code&gt;permission denied for table&lt;/code&gt; (&lt;a href="https://supabase.com/docs/guides/troubleshooting/database-api-42501-errors" rel="noopener noreferrer"&gt;Supabase: 42501 errors&lt;/a&gt;). Not every 42501 is a missing grant: Supabase also lists restricted schemas, custom schemas without API grants, column-level restrictions, and RLS blocking an insert or update. If the message says "new row violates row-level security policy", see our &lt;a href="https://builtsafely.com/fixes/new-row-violates-rls/" rel="noopener noreferrer"&gt;RLS error fix&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you limited updates to certain columns on purpose, for example so users can't change their own role, a 42501 on those columns is that protection working. See &lt;a href="https://builtsafely.com/guides/editable-access-field/" rel="noopener noreferrer"&gt;Never let users edit the field your access rule trusts&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What not to do
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Docs-only&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't disable RLS.&lt;/li&gt;
&lt;li&gt;Don't grant everything to &lt;code&gt;anon&lt;/code&gt; without policies.&lt;/li&gt;
&lt;li&gt;Don't move the secret or &lt;code&gt;service_role&lt;/code&gt; key into the frontend (&lt;a href="https://supabase.com/docs/guides/getting-started/api-keys" rel="noopener noreferrer"&gt;Supabase: API keys&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat grant + enable RLS + policies as one unit in the same migration (&lt;a href="https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically" rel="noopener noreferrer"&gt;Supabase changelog&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy this fix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Replace notes with your table. Signed-out visitors get nothing.&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;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;notes&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;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;notes&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;-- ...plus one policy per operation (see our RLS guide)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grant only what each role needs, and keep RLS on. Policy examples: &lt;a href="https://builtsafely.com/guides/supabase-rls/" rel="noopener noreferrer"&gt;Supabase row-level security, explained for builders&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://builtsafely.com/fixes/new-row-violates-rls/" rel="noopener noreferrer"&gt;new row violates row-level security policy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://builtsafely.com/guides/supabase-oct30-grants/" rel="noopener noreferrer"&gt;Supabase's Oct 30 change: new tables need a GRANT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://builtsafely.com/fixes/infinite-recursion-policy/" rel="noopener noreferrer"&gt;infinite recursion detected in policy for relation (42P17)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Educational, not a security audit. Platforms change: check the linked sources. &lt;a href="https://builtsafely.com/disclaimer/" rel="noopener noreferrer"&gt;Disclaimer&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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