PostgreSQL

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.

Mahdi Ben RhoumaMahdi Ben Rhouma
8 min read

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.

text
Failed to create pg.columns: must be the owner of event trigger pgsodium_trg_mask_update

Why 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#

  1. Unblock yourself immediately: use the SQL editor, not the Table Editor. Open SQL Editor in the Supabase dashboard and run the CREATE TABLE or ALTER TABLE statement you were trying to do through the UI:

    sql
    create 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.

  2. Do not try to reassign ownership yourself. ALTER EVENT TRIGGER pgsodium_trg_mask_update OWNER TO postgres looks 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 in supabase/supabase#12952.

  3. If you have no Vault secrets or encrypted columns yet, reinitialise the extensions. This resets pgsodium and supabase_vault with correct ownership, per the Supabase engineer's own fix in the same thread:

    sql
    drop 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 the vault schema and any column using SECURITY LABEL ... ENCRYPT WITH KEY.

  4. 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.

  5. If the named trigger is different (for example pgrst_ddl_watch or grant_pg_cron_access instead of pgsodium_trg_mask_update), you're looking at the same class of bug — Supabase's own event triggers normally owned by supabase_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:

sql
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

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote