Supabase pgsodium_trg_mask_update: Fix Owner Error
Creating or editing a table in the Supabase dashboard throws a permission error that names a trigger you have never heard of. It is not a missing grant — it is Vault's pgsodium extension holding an event trigger your role does not own. Here is the workaround that keeps you working today and the fix that resets ownership for good.
TL;DR#
must be the owner of event trigger pgsodium_trg_mask_update comes from Supabase's Vault
extension (pgsodium), which installs a Postgres event trigger owned by the internal
supabase_admin role. On a subset of projects, that ownership link is broken, so Supabase's
dashboard can't perform the owner-only step it needs when you create or edit a table. The
immediate workaround is to run the same SQL directly in the SQL editor instead of the
Table Editor UI. The underlying fix — only if you have no Vault secrets or encrypted columns to
preserve — is to drop and recreate the pgsodium and supabase_vault extensions, which
reinitialises them with the correct ownership.
Failed to create pg.columns: must be the owner of event trigger pgsodium_trg_mask_updateWhy it happens#
This error was reported on Stack Overflow in February 2023: a developer on Supabase's free tier could no longer create or edit tables from the dashboard, with no Vault feature visible in their settings to disable it (Stack Overflow #75529600).
pgsodium_trg_mask_update is not a table trigger you created — it's a Postgres event
trigger, a database-wide object that fires on DDL commands. Supabase's Vault feature installs
it to manage column-level encryption (SECURITY LABEL ... ENCRYPT WITH KEY). A related
Postgres-fork issue confirms the normal setup: on a healthy project, pgsodium_trg_mask_update
and several other Supabase-installed event triggers are owned by the internal supabase_admin
role, not by the postgres role a project owner connects as (supabase/postgres#1437).
A separate, earlier GitHub issue tracks the exact error from this brief and traces its cause.
Multiple users hit must be owner of event trigger pgsodium_trg_mask_update while trying to
enable column encryption, and one tried the textbook Postgres fix — ALTER EVENT TRIGGER pgsodium_trg_mask_update OWNER TO postgres — which failed with the same error, because you
need to already be the owner (or a superuser) to reassign ownership in the first place
(supabase/supabase#12952). A Supabase
engineer (michelp) explained the root cause in that thread: on an older version of Supabase's
platform, a dump/restore bug corrupted the ownership of Vault's objects on some existing
projects. Projects created fresh, after the underlying bug was fixed, don't show it.
That explains why the error can surface on something as unrelated-looking as the dashboard's
Table Editor: Supabase's dashboard doesn't send your CREATE TABLE or ALTER TABLE as raw SQL.
It goes through Supabase's metadata/schema layer first, and on an affected project that layer's
own operations hit the broken ownership before your table change is applied. Typing the exact
same statement into the SQL editor instead often succeeds, because it skips that layer —
which is exactly what the accepted answer on the original question reports: no fix for the
Table Editor itself, but the SQL editor creates and edits tables fine.
Fix#
-
Unblock yourself immediately: use the SQL editor, not the Table Editor. Open SQL Editor in the Supabase dashboard and run the
CREATE TABLEorALTER TABLEstatement you were trying to do through the UI:sqlcreate table public.orders ( id bigint generated always as identity primary key, customer_id uuid not null, total numeric(10, 2) not null, created_at timestamptz not null default now() );This is the only workaround the original Stack Overflow thread found for the Table Editor failure itself, and it does not touch Vault or pgsodium at all.
-
Do not try to reassign ownership yourself.
ALTER EVENT TRIGGER pgsodium_trg_mask_update OWNER TO postgreslooks like the standard Postgres fix for an ownership mismatch, but it fails with the same "must be the owner" error, because changing an object's owner is itself an owner-only operation — you cannot bootstrap ownership from a role that doesn't already have it. This was tried and confirmed to fail insupabase/supabase#12952. -
If you have no Vault secrets or encrypted columns yet, reinitialise the extensions. This resets
pgsodiumandsupabase_vaultwith correct ownership, per the Supabase engineer's own fix in the same thread:sqldrop view vault.decrypted_secrets; drop extension supabase_vault; drop extension pgsodium; create extension pgsodium; create extension supabase_vault;This is destructive to anything already stored in Vault or encrypted with
pgsodium: it removes thevaultschema and any column usingSECURITY LABEL ... ENCRYPT WITH KEY. -
If you already have encrypted data or Vault secrets, preserve it first. Export or decrypt the values you need to keep (a one-off script using the existing session, while the extensions still work for reads, or a database dump restricted to the affected tables), run step 3, then re-insert the data so it gets re-encrypted under the freshly created extension. Skip this step only if the project is new enough that nothing depends on Vault yet.
-
If the named trigger is different (for example
pgrst_ddl_watchorgrant_pg_cron_accessinstead ofpgsodium_trg_mask_update), you're looking at the same class of bug — Supabase's own event triggers normally owned bysupabase_admin(supabase/postgres#1437) — but recreating a different extension is not interchangeable with step 3. Open a support ticket with Supabase rather than dropping extensions blindly.
Verify the fix#
Confirm the SQL editor path works first — run a throwaway create table public._verify_fix (id int); from the SQL editor, then drop table public._verify_fix;. If that succeeds while
the same statement fails through the Table Editor UI, you've confirmed the dashboard's
metadata layer is the one hitting the ownership issue, not your SQL or your table.
After reinitialising the extensions (step 3), check ownership directly:
select evtname, evtowner::regrole
from pg_event_trigger
where evtname = 'pgsodium_trg_mask_update';It should return supabase_admin as the owner. Then retry the original table creation or edit
through the Table Editor UI — it should no longer raise the error, because the dashboard's
layer and your session now agree on who owns the trigger.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
If GRANTs are the next error#
Recreating pgsodium/supabase_vault resets ownership, but it does not automatically grant
execution rights on the encryption functions to your API roles. If your next error after this
fix is permission denied for function crypto_aead_det_encrypt (or _decrypt) when the
anon or authenticated role tries to read an encrypted column, that's expected: grant
execution on the specific function you're calling, for example grant execute on function pgsodium.crypto_aead_det_encrypt(bytea, bytea, uuid, bytea) to authenticated;. More generally,
Supabase's managed schemas (auth, vault) are intentionally locked down from the API roles —
see Supabase 42501: permission denied for schema auth
for the same "it's not a missing GRANT you forgot" pattern on the auth schema.
Avoiding the dashboard's metadata layer going forward#
Once you know the Table Editor UI can be the thing failing — not your schema — it's worth
moving routine schema changes to SQL you control directly, especially on a project that has
shown this bug once. Adding or extending a Postgres enum is a good example: doing it with
ALTER TYPE ... ADD VALUE in a migration sidesteps the dashboard's column editor entirely (see
Supabase Enums: ALTER TYPE ADD VALUE in Migrations).
Once you're working from the SQL editor or psql, you can also check what actually got created
with \dt and \d table_name rather than trusting the dashboard's table list — see
PostgreSQL SHOW TABLES / DESCRIBE TABLE (psql + Supabase)
for the exact commands.
The underlying lesson from this error is narrower than it first looks: Supabase's dashboard is a client of your database, not a special code path immune to the rules the database enforces. When it fails with a Postgres ownership error, the fix is the same one you'd apply to any Postgres ownership problem — work out who really owns the object, and fix that, rather than assuming the dashboard itself is broken.
Further reading: Supabase db push: must be owner of table (SQLSTATE 42501). Also see Fix: Supabase local logs "Backend error" (and old 502s).
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix: Supabase local logs "Backend error" (and old 502s)
The same local Studio Logs pipeline, broken three different ways since 2024: here is which one you are on, and the fix for each.
Supabase db push: must be owner of table (SQLSTATE 42501)
Why a migration that runs fine locally is refused on the remote project, which owner fixes work from the postgres role today, and when only Supabase support can help.
Why Your Supabase Queries Are Slow (And How to Fix)
Slow Supabase queries kill your app feel and inflate your bill. Here are the six causes I keep seeing in production apps, and the exact SQL and code fixes for each one.


