Describe the bug
supabase --experimental db schema declarative sync --no-apply -f <name> fails on CLI 2.116.0 with:
{"_tag":"Error","error":{"code":"LegacyPgDeltaEngineError","message":"Declarative schema planning failed: permission denied for table schema_migrations"}}
It fails on an unmodified checkout whose migrations and declarative files are in sync, and on the same tree that generated a migration successfully with the same CLI the day before. The migrations shadow builds fine; the failure is in the second (declarative) shadow.
The shadow database's Postgres log shows the failing statement and role:
postgres@postgres ERROR: permission denied for table schema_migrations
postgres@postgres STATEMENT: SELECT EXISTS (SELECT 1 FROM "_realtime"."schema_migrations" LIMIT 1) AS has
Cause (from reading the bundled engine)
The declarative load step checks that the schema files contain no data statements by listing every regular, non-extension table in the shadow (pg_class with relkind = 'r') and running SELECT EXISTS (SELECT 1 FROM <table> LIMIT 1) on each, as the postgres role. The check runs before and after the files are applied (the "managed user table ... contains rows after loading the declarative files" validation).
The shadow's Realtime sidecar creates _realtime.schema_migrations, _realtime.tenants and _realtime.extensions as supabase_admin with no grants (relacl is null), and postgres is not a superuser in the local image. So as soon as Realtime has finished its own migrations before the probe runs, the probe throws. Whether that happens is a race with the sidecar's boot, which is why the same tree can pass one day and fail the next. In the run above Realtime finished about 2 s after the shadow was ready and the probe ran about 4 s after.
The _realtime schema is already on the engine's managed-schema list, so the probe has no reason to touch it.
To Reproduce
- CLI 2.116.0 (Homebrew or npm), Docker Desktop on macOS,
[realtime] enabled = true (the default) in config.toml, schema_paths = ["./schemas/*.sql"].
- Any project whose migrations and declarative files are in sync.
supabase --experimental db schema declarative sync --no-apply -f probe
- Observe the error above.
--no-cache and --schema public do not change it.
Confirming the cause: set [realtime] enabled = false, rerun step 3, it reports No schema changes found.
Expected behavior
The data-statement probe should skip managed/platform schemas (or tables the connecting role cannot read), or run as supabase_admin, so the declarative sync does not depend on the Realtime sidecar's boot timing.
System information
- OS: macOS 26 (Darwin 25.6.0), arm64
- Supabase CLI 2.116.0
- Docker Desktop, engine 28.0.1
- Postgres image
supabase/postgres:17.6.1.071, Realtime v2.129.3
Workaround
Disable Realtime in config.toml for the duration of the sync, then revert the file.
Describe the bug
supabase --experimental db schema declarative sync --no-apply -f <name>fails on CLI 2.116.0 with:It fails on an unmodified checkout whose migrations and declarative files are in sync, and on the same tree that generated a migration successfully with the same CLI the day before. The migrations shadow builds fine; the failure is in the second (declarative) shadow.
The shadow database's Postgres log shows the failing statement and role:
Cause (from reading the bundled engine)
The declarative load step checks that the schema files contain no data statements by listing every regular, non-extension table in the shadow (
pg_classwithrelkind = 'r') and runningSELECT EXISTS (SELECT 1 FROM <table> LIMIT 1)on each, as thepostgresrole. The check runs before and after the files are applied (the "managed user table ... contains rows after loading the declarative files" validation).The shadow's Realtime sidecar creates
_realtime.schema_migrations,_realtime.tenantsand_realtime.extensionsassupabase_adminwith no grants (relaclis null), andpostgresis not a superuser in the local image. So as soon as Realtime has finished its own migrations before the probe runs, the probe throws. Whether that happens is a race with the sidecar's boot, which is why the same tree can pass one day and fail the next. In the run above Realtime finished about 2 s after the shadow was ready and the probe ran about 4 s after.The
_realtimeschema is already on the engine's managed-schema list, so the probe has no reason to touch it.To Reproduce
[realtime] enabled = true(the default) inconfig.toml,schema_paths = ["./schemas/*.sql"].supabase --experimental db schema declarative sync --no-apply -f probe--no-cacheand--schema publicdo not change it.Confirming the cause: set
[realtime] enabled = false, rerun step 3, it reportsNo schema changes found.Expected behavior
The data-statement probe should skip managed/platform schemas (or tables the connecting role cannot read), or run as
supabase_admin, so the declarative sync does not depend on the Realtime sidecar's boot timing.System information
supabase/postgres:17.6.1.071, Realtimev2.129.3Workaround
Disable Realtime in
config.tomlfor the duration of the sync, then revert the file.