PostgreSQL raises this error when you attempt a write operation (INSERT, UPDATE, DELETE, CREATE, etc.) on a standby/replica server that is in recovery mode. Only the primary/master server accepts write transactions. Route write operations to the primary server to resolve this.
The "Cannot execute in recovery" error occurs when PostgreSQL detects an attempt to execute a command that modifies the database on a standby/replica server. Standby servers are in continuous recovery mode, applying Write-Ahead Log (WAL) records from the primary server. This read-only state prevents write operations from executing. PostgreSQL standby servers (also called replicas) are designed for read-only access and high availability. When you attempt operations like INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, TRUNCATE, or VACUUM on a standby server, PostgreSQL rejects the operation to maintain replication consistency. The error occurs because the standby is continuously recovering/replaying changes from the primary and cannot accept new write transactions.
First, check if your connection is pointing to a standby or primary server using the pg_is_in_recovery() function:
SELECT pg_is_in_recovery();If this returns true, you are connected to a standby/replica server. If it returns false, you are connected to the primary/master server.
Update your application connection string or database configuration to point to the primary/master server instead of the standby.
For direct connections:
primary_host = your-primary-server.example.com
primary_port = 5432For AWS RDS:
Use the writer endpoint instead of the read replica endpoint:
# Wrong (read replica):
postgres://read-replica-instance.abc123.us-east-1.rds.amazonaws.com:5432/dbname
# Correct (primary):
postgres://my-instance.abc123.us-east-1.rds.amazonaws.com:5432/dbnameIf using a connection pool or application framework, ensure read-only queries can use replicas while write operations always use the primary.
Example with node-postgres (pg):
const primaryPool = new Pool({
host: 'primary.example.com',
user: 'postgres',
password: 'password',
database: 'mydb',
});
const replicaPool = new Pool({
host: 'replica.example.com',
user: 'postgres',
password: 'password',
database: 'mydb',
});
// Use primaryPool for write operations
await primaryPool.query('INSERT INTO users VALUES (...)');
// Use replicaPool for read-only operations
const result = await replicaPool.query('SELECT * FROM users');If using a load balancer or AWS Route 53, verify that your database endpoint routes to the primary server for write operations.
For AWS Route 53:
- Ensure write operations go to the primary DNS endpoint
- Configure read replicas with separate endpoints for read-only traffic
For HAProxy or pgBouncer:
# pgBouncer configuration example
[databases]
mydb_primary = host=primary.example.com dbname=mydb
mydb_replica = host=replica.example.com dbname=mydbAfter updating your connection to the primary server, test a write operation:
INSERT INTO test_table (name) VALUES ('test');
UPDATE test_table SET name = 'updated' WHERE id = 1;
DELETE FROM test_table WHERE id = 1;These operations should now execute successfully without the "Cannot execute in recovery" error.
Hot Standby and Read Queries: PostgreSQL standby servers in "hot standby" mode can execute SELECT queries while continuously recovering from the primary. However, any transaction that modifies data (writes) will always be rejected. In streaming replication setups, you can reduce query conflicts on standby servers by adjusting max_standby_streaming_delay (default 30 seconds) and enabling hot_standby_feedback in postgresql.conf. These settings help prevent query cancellation due to WAL conflicts. For AWS Aurora, use the cluster endpoint for writes and individual instance endpoints for reads. For self-managed PostgreSQL with pgBouncer, configure separate connection pools for primary (read-write mode) and replicas (read-only mode) to ensure queries are routed correctly.
insufficient columns in unique constraint for partition key
How to fix "insufficient columns in unique constraint for partition key" in PostgreSQL
ERROR 42501: must be owner of table
How to fix "must be owner of table" in PostgreSQL
trigger cannot change partition destination
How to fix "Trigger cannot change partition destination" in PostgreSQL
SSL error: certificate does not match host name
SSL error: certificate does not match host name in PostgreSQL
No SSL connection
No SSL connection to PostgreSQL