---
title: "Supabase 42501 permission denied：原因はgrantでRLSではない"
description: "Supabaseがテーブルで42501 permission deniedを返す理由、2026年10月30日に変わること、それを直すgrant・RLS・ポリシーを、ローカルで再現して紹介します。"
url: "https://madsalt.tech/ja/notes/supabase-42501-permission-denied/"
author: "Denys Zaichenko"
language: "ja"
published: 2026-10-26
---

# Supabase 42501 permission denied：原因はgrantでRLSではない

TL;DR

`42501 permission denied for table …`は、そのロールにテーブルへのgrantがないことを意味します。そのため、RLSポリシーが実行される前に、Postgresがリクエストを止めます。2026年10月30日から、Supabaseは既存プロジェクトの`public`の新しいテーブルに自動でgrantしなくなります。それ以降に作られたテーブルは、grantするまで42501を返します。直し方は、3つの文をセットにすることです。ロールに必要な操作へのgrant、`enable row level security`、そしてポリシーです。RLSなしでgrantだけを行うと、テーブルは全員に開いてしまいます。

私は、公開されているすべてのテーブルのgrantを読む無料の[Supabaseセキュリティチェッカー](https://madsalt.tech/ja/tools/supabase-security-check/)を作りました。42501は、grantが逆方向に間違っているとき、つまり多すぎるのではなく少なすぎるときに出るエラーです。ここでは、それが何を意味するかと、再現して分かったことを説明します。

## Supabaseのエラー42501は何を意味する？

42501は、権限不足を表すPostgresのコードです。クライアントがテーブルに触れる前に、Postgresは2つの層を確認します。grantは、そのロールがその操作を実行できるかを決め、RLSポリシーは、どの行かを決めます。grantがなければ、まずエラーとして失敗します。どの行にも一致しないポリシーは、空のリストとして静かに失敗します。だから42501を見たら、ポリシーではなくgrantを見てください。

`supabase-js`の裏にあるData APIのPostgRESTは、エラーをヒント付きでそのまま返します。私のローカルスタック（Supabase CLI 2.109.1、Postgres 17.6、PostgREST 14.14）では、`anon`へのgrantがないテーブルにpublishable keyでリクエストすると、HTTP 401と次の内容が返りました。

```json
{
  "code": "42501",
  "details": null,
  "hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.orders TO anon;",
  "message": "permission denied for table orders"
}
```

## 2026年10月30日以降、新しいテーブルで42501が出るのはなぜ？

これまで、`public`の新しいテーブルには、デフォルト権限により`anon`、`authenticated`、`service_role`へ`select`、`insert`、`update`、`delete`が付与されていました。そのため、保護されているかどうかにかかわらず、作成した瞬間からData API経由で到達できました。Supabaseの[changelog](https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically)は、このデフォルトを取り除きます。2026年4月28日からオプションで、2026年5月30日から新規プロジェクトのデフォルトに、そして2026年10月30日にすべての既存プロジェクトへ適用されます。既存のテーブルはgrantを維持します。

切り替えで他に何が壊れるか、事前の確認方法は[Supabase 10月30日のgrant変更：壊れる点と確認方法](https://madsalt.tech/ja/blog/supabase-october-30-grant-change/)にまとめました。

changelogにある2つの`alter default privileges … revoke`の文で、同じ変更を自分のローカルプロジェクトに適用し、新しいテーブル`invoices`を作りました。

![2026年10月30日以降、新しいテーブルが返すもの。新しいデフォルト権限のローカルSupabase、リクエストは各1回。Publishable key、grantなし：create table → grantなし → 42501, HTTP 401、Secret key、grantなし：create table → grantなし → 42501, HTTP 403、grant selectとRLS有効化のあと：grant select → RLSオン → 0行、HTTP 200。](https://madsalt.tech/charts/ja/supabase-42501-permission-denied.svg)

*Supabaseのchangelogにあるデフォルト権限を適用したローカルのSupabaseスタックでの私の再現、2026年10月23日。*

| 新しいテーブルへのリクエスト                                       | 結果                                                                |
| ------------------------------------------------------------------ | ------------------------------------------------------------------- |
| Publishable key、grantなし                                         | HTTP 401、42501、ヒントは`GRANT SELECT ON public.invoices TO anon;` |
| Secret key、grantなし                                              | HTTP 403、42501、ヒントは`… TO service_role;`                       |
| Publishable key、`grant select`と`enable row level security`のあと | HTTP 200、ポリシーが行を許可するまで`[]`                            |

驚いたことが2つあります。1つ目は、secret keyも拒否されることです。`service_role`はRLSを迂回しますが、それでもgrantは必要なので、新しいテーブルに書き込むEdge Functionやサーバーのジョブも、同じように壊れます。2つ目は、revokeで取り消されるのはその4つの操作だけだということです。新しいテーブルには、`anon`、`authenticated`、`service_role`向けの`REFERENCES`、`TRIGGER`、`TRUNCATE`が付いたままでした。`TRUNCATE`にはどのポリシーも適用されません。

## テーブルを開けずに42501を直すには？

grant、RLS、ポリシーを1つの変更として、1つのマイグレーションにまとめ、各ロールには必要なものだけを付与します。

```sql
revoke all on table public.invoices from anon, authenticated;
grant select, insert, update on table public.invoices to authenticated;
grant select, insert, update, delete on table public.invoices to service_role;

alter table public.invoices enable row level security;

create policy "Owners read their invoices"
on public.invoices for select
to authenticated
using ((select auth.uid()) = user_id);
```

最初の`revoke all`で、テーブルと一緒に付いてきた`TRUNCATE`、`TRIGGER`、`REFERENCES`を消します。42501に`grant all on all tables in schema public to anon`で答えてはいけません。それは古いデフォルトを、すべてのテーブルに一度に戻すことで、RLSのないテーブルは、公開キーを持つ誰にでも読めるようになります。

AIツールがテーブルを作るなら、grant、RLS、ポリシーを同じマイグレーションで出力するよう指示してください。エラーのヒントは、エージェントがgrantを自分で直せるように書かれていますが、ヒントが挙げるのはgrantだけで、一緒に付けるべきRLSには触れていません。

## チェッカーはこれをどう見る？

チェッカーの4つのルールがgrantの層を読みます。「新しいオブジェクトを API に開くデフォルト権限」は、新しいテーブルを今も自動でgrantするプロジェクトを指摘します。「匿名の訪問者に残っている未使用の書き込み権限」は、どのポリシーも使っていない`anon`の書き込みgrantを見つけます。「サインイン中のユーザーに不要なテーブル権限」は、上のような`TRUNCATE`、`TRIGGER`、`REFERENCES`を見つけます。「実行されないポリシー：ロールに権限がない」は、そのコマンドのgrantを持たないロールをポリシーが指定しているテーブルを一覧にします。10月30日以降の新しいテーブルは、まさにこの状態です。grantのあともRLSが重要な理由は[Supabase RLSのテスト方法](https://madsalt.tech/ja/blog/how-to-test-supabase-rls/)にあり、アプリ全体でgrantとポリシーがどうなるかは[Lovableアプリのセキュリティチェックリスト](https://madsalt.tech/ja/blog/lovable-app-security-checklist/)にあります。
