PostgreSQL 42501 — Cannot alter table owned by custom NOLOGIN role in Supabase #51809
Replies: 3 comments
|
In Supabase, postgres isn't a true SUPERUSER (supabase_admin is) — it relies on CREATEROLE. Starting in Postgres 16, when a CREATEROLE user creates a new role, it gets ADMIN OPTION by default, but SET and INHERIT default to false (which is why pg_has_role(current_user, 'onefix_recruitment_api_owner', 'SET') is returning false for you). Since postgres still has ADMIN rights over custom roles, the cleanest way to run your migration without touching ownership or RLS is to grant SET temporarily inside the transaction, switch to the NOLOGIN owner via SET LOCAL ROLE, run the DDL, and revoke it before committing: BEGIN;
GRANT onefix_recruitment_api_owner TO postgres WITH SET TRUE, INHERIT FALSE;
SET LOCAL ROLE onefix_recruitment_api_owner;
-- Run your DDL as the actual table/function owner
ALTER TABLE onefix_recruitment.applications
ADD COLUMN col_1 text,
ADD COLUMN col_2 text,
ADD COLUMN col_3 text;
CREATE OR REPLACE FUNCTION onefix_recruitment.your_function()
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
-- updated logic
END;
$$;
RESET ROLE;
REVOKE SET OPTION FOR onefix_recruitment_api_owner FROM postgres;
COMMIT;Because the GRANT and REVOKE SET OPTION happen inside the same transaction block, no permanent privilege changes are left behind after COMMIT, table/function ownership stays with onefix_recruitment_api_owner, and FORCE RLS remains untouched. (Quick check: if for some reason GRANT ... TO postgres also throws a permission error, run SELECT pg_has_role('postgres', 'onefix_recruitment_api_owner', 'ADMIN'); — as long as that returns true, the transaction block above will work out of the box.) |
|
devcaiada's answer is the right core: in Postgres 16, a CREATEROLE user gets ADMIN OPTION on roles it creates, but SET defaults to false — so What I'd add is the self-contained migration-file version — everything in one transaction so it works unchanged in CI (Supabase CLI BEGIN;
GRANT onefix_recruitment_api_owner TO postgres WITH SET TRUE, INHERIT FALSE;
SET LOCAL ROLE onefix_recruitment_api_owner;
ALTER TABLE onefix_recruitment.applications
ADD COLUMN col_1 text,
ADD COLUMN col_2 text,
ADD COLUMN col_3 text;
CREATE OR REPLACE FUNCTION onefix_recruitment.your_function()
LANGUAGE plpgsql
SET search_path = ''
AS $$
BEGIN
-- updated logic
END;
$$;
RESET ROLE;
REVOKE onefix_recruitment_api_owner FROM postgres;
COMMIT;Three gotchas that bite people with this owner-role pattern:
One check before the grant: make sure I wrote up this whole failure class — dedicated owner roles that quietly break migrations and definer functions — in "You Gave Your SECURITY DEFINER Function a Dedicated Owner Role. It Broke.", and there's a pre-flight checklist for exactly these 42501/ownership cases in the Supabase Migration Rescue Kit (status-check query, repair playbook, pre-deploy checklist). |
|
Temporary SELECT
pg_has_role(current_user, 'onefix_recruitment_api_owner',
'MEMBER WITH ADMIN OPTION') AS can_administer,
pg_has_role(current_user, 'onefix_recruitment_api_owner',
'SET') AS can_set_role;Plain Before changing membership, record the relevant Also use the actual function definition, including its return type and security attributes. The second example omits I checked these distinctions on disposable PostgreSQL 16.14. An ordinary member failed the admin-option check, |
Uh oh!
There was an error while loading. Please reload this page.
Hello Supabase Community,
We need guidance on a PostgreSQL table ownership issue in an isolated Supabase development project.
Problem:
We are trying to execute a database migration that adds three columns to an existing table and updates a restricted PostgreSQL function.
The migration fails with:
ERROR 42501: must be owner of table applicationsCurrent configuration:
onefix_recruitment.applicationsonefix_recruitment_api_ownerpostgrespg_has_role(current_user, 'onefix_recruitment_api_owner', 'SET')returnsfalseWhat we need:
A supported, secure method to execute this migration without:
Is there a recommended Supabase procedure for executing owner-authorized DDL when the table is owned by a custom NOLOGIN role that the SQL Editor's
postgressession cannot assume?We would appreciate clear instructions on the safest supported approach.
Thank you.
All reactions