PostgreSQL "permission denied for schema" error occurs when a user lacks necessary privileges to access or create objects within a schema. Especially common in PostgreSQL 15+ where public schema permissions changed, it requires granting explicit USAGE and CREATE privileges. Fix by using GRANT statements with appropriate permissions.
PostgreSQL "permission denied for schema" is a privilege error that occurs when the connected user lacks the required permissions to perform operations on a specific schema. Schemas are namespace containers in PostgreSQL that organize tables, views, functions, and other objects. When PostgreSQL encounters a query or operation targeting a schema where the current user does not have the necessary privileges, it immediately fails with this error. The error can occur on schema-level operations like CREATE TABLE, SELECT from existing tables, or even just navigating to objects within that schema. PostgreSQL 15 introduced a significant security change: the default permissions of the public schema were restricted. Before PostgreSQL 15, any user could create objects in the public schema. Starting with version 15, only the database owner can create objects in the public schema without explicit permission grants. This change was made to prevent privilege escalation attacks where malicious users could inject code into the shared public schema. Permission denied errors manifest when users try to create tables, insert data, or even query existing tables without the proper USAGE or CREATE grants on the schema.
First, understand which user is experiencing the error and which schema they need to access. Connect to PostgreSQL as a superuser and check the current state.
-- Check current user and database:
SELECT current_user, current_database();
-- List all schemas:
\dn
-- or:
SELECT schema_name FROM information_schema.schemata;
-- Check which schema the user is trying to access:
SHOW search_path; -- Shows the default schema search orderIdentify the specific schema name (typically "public" for the default schema) that is causing the permission error.
You must have superuser or schema owner privileges to grant permissions to other users. Connect with a superuser account to make changes.
# Connect as superuser (postgres user is typically the default superuser)
psql -U postgres -d your_database
# Or if using a connection string:
psql postgresql://admin@localhost/your_databaseOnce connected as a privileged user, you can grant the necessary permissions to the affected user.
The USAGE privilege allows the user to access objects (tables, views, functions) that exist within the schema. This is the minimum required privilege.
-- Grant USAGE privilege on the public schema:
GRANT USAGE ON SCHEMA public TO your_username;
-- Or for a custom schema:
GRANT USAGE ON SCHEMA your_schema TO your_username;
-- Grant to a role (if the user is part of a role):
GRANT USAGE ON SCHEMA public TO your_role;After running this command, the user will be able to query existing tables in the schema, but still cannot create new objects.
If the user needs to create new tables, views, functions, or other objects in the schema, grant the CREATE privilege. This is required for migrations and DDL operations.
-- Grant CREATE privilege on the public schema:
GRANT CREATE ON SCHEMA public TO your_username;
-- Grant both USAGE and CREATE together:
GRANT USAGE, CREATE ON SCHEMA public TO your_username;
-- Or grant ALL privileges (USAGE + CREATE + other administrative privileges):
GRANT ALL ON SCHEMA public TO your_username;The CREATE privilege is essential for ORM migrations (Prisma, Sequelize, etc.) to create tables and indexes.
Even with schema privileges, users still need explicit permissions on individual tables. Grant appropriate privileges on all existing tables.
-- Grant SELECT, INSERT, UPDATE, DELETE on all existing tables:
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO your_username;
-- Or for read-only access:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO your_username;
-- Grant usage on sequences (required for auto-increment columns):
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO your_username;
-- Grant execute on functions:
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO your_username;These grants apply to existing objects. For new objects created in the future, use ALTER DEFAULT PRIVILEGES.
Configure default privileges so that new objects automatically inherit the correct permissions without needing to grant them manually each time.
-- As the schema owner or superuser, set defaults:
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO your_username;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE ON SEQUENCES TO your_username;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO your_username;These rules automatically apply to objects created after the ALTER DEFAULT PRIVILEGES statement. Existing objects are not affected.
Query the system catalogs to confirm the user now has the necessary privileges.
-- Check schema privileges:
SELECT grantee, privilege_type
FROM information_schema.role_schema_grants
WHERE schema_name = 'public' AND grantee = 'your_username'
ORDER BY privilege_type;
-- Check table privileges:
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_schema = 'public' AND grantee = 'your_username'
ORDER BY table_name, privilege_type;
-- For a comprehensive view of what the user can do:
\dp public.*Verify that USAGE and CREATE appear in the results for the schema. For tables, verify SELECT, INSERT, UPDATE, DELETE are listed.
Reconnect using the user account that was experiencing the error to verify the fix works.
# Connect as the user that had permission errors:
psql -U your_username -d your_databaseThen try the operations that previously failed:
-- Test schema access:
SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';
-- Test creating a table (if CREATE was granted):
CREATE TABLE test_table (id INT, name VARCHAR(100));
-- Test inserting data:
INSERT INTO test_table VALUES (1, 'test');
-- Clean up:
DROP TABLE test_table;If these commands succeed without permission errors, the issue is resolved.
For PostgreSQL 15 migrations: if you are upgrading from PostgreSQL 14 or earlier, the breaking change in public schema permissions often catches teams off guard during CI/CD deployments. When running migrations with a non-superuser account, explicitly grant permissions before running migration tools. Many ORMs have built-in superuser checks that temporarily switch to superuser for migrations—check your ORM configuration.
Row-level security (RLS) can also cause permission-like errors. If a user has table-level permissions but RLS policies restrict row access, they will still see permission denied errors. Check for RLS policies using SELECT * FROM pg_policies WHERE tablename = 'your_table'.
For multi-tenant systems, consider using separate schemas per tenant rather than row-level filters. This isolates data more effectively and simplifies permission management.
When using connection pooling (PgBouncer, pgpool), ensure the pooler user has sufficient privileges and that prepared statement parameters are properly handled—some pooling modes restrict privilege escalation during pooled sessions.
Always follow the principle of least privilege: grant only the minimum permissions needed for each user role. Never use superuser credentials in application code. Create dedicated application users with limited schema and table permissions, separate from admin accounts.
ERROR: syntax error at end of input
Syntax error at end of input in PostgreSQL
Bind message supplies N parameters but prepared statement requires M
Bind message supplies N parameters but prepared statement requires M in PostgreSQL
Multidimensional arrays must have sub-arrays with matching dimensions
Multidimensional arrays must have sub-arrays with matching dimensions
ERROR: value too long for type character varying
Value too long for type character varying
insufficient columns in unique constraint for partition key
How to fix "insufficient columns in unique constraint for partition key" in PostgreSQL