The new error message is not great:

postgres=# select pg_replication_origin_session_setup('myorigin', 12345678);
ERROR: could not find replication state slot for replication origin with OID 1 which was acquired by 12345678

Firstly, replication origin is not an OID. Secondly, it's a little confusing because the "replication state slot" is in fact present. However, it's currently inactive, i.e. not "acquired" by the given PID.

I propose to change that to:

postgres=# select pg_replication_origin_session_setup('myorigin', 12345678);
ERROR:  replication origin with ID 1 is not active for PID 12345678

That's more in line with this neighboring message:

ERROR:  replication origin with ID 1 is already active for PID 701228


I also wonder if the error code is appropriate. That error uses ERRCODE_OBJECT_IN_USE, but if the problem is that the origin is currently *not* active, that seems backwards. I didn't change that in the attached patch, but it's something to think about.


The second patch rearranges the if-else statements to check those conditions. I found the current logic hard to follow, this makes them feel more natural, in my opinion at least. It has one user-visible effect: If you call the function with acquired_pid != 0 and the origin has no state slot, *and* there are no free slots, you previously got this error:

postgres=# select pg_replication_origin_session_setup('other', 123);
ERROR: could not find free replication state slot for replication origin with ID 2
HINT:  Increase "max_active_replication_origins" and try again.

Now you get this:

postgres=# select pg_replication_origin_session_setup('other', 123);
ERROR:  cannot use PID 123 for inactive replication origin with ID 2

Both error messages are more or less appropriate in that situation, but I think the new behavior is slightly better. The fact that the origin is inactive feels like the bigger problem here.

- Heikki
From 233e54b0b4dbdaa84910fb7cc453877391865839 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Tue, 3 Feb 2026 20:33:23 +0200
Subject: [PATCH 1/2] Improve error message

- Use the same wordings and terms as the neighboring messages
- Use 'curstate->roident' instead of 'node'. We've checked that they're
  equal, but let's be consistent with the neighboring ereports().
---
 src/backend/replication/logical/origin.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index c3271a6fd0e..ec040a039d3 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1197,8 +1197,8 @@ replorigin_session_setup(ReplOriginId node, int acquired_by)
 		{
 			ereport(ERROR,
 					(errcode(ERRCODE_OBJECT_IN_USE),
-					 errmsg("could not find replication state slot for replication origin with OID %u which was acquired by %d",
-							node, acquired_by)));
+					 errmsg("replication origin with ID %d is not active for PID %d",
+							curstate->roident, acquired_by)));
 		}
 
 		/*
-- 
2.47.3

From 57b2e789b40297ba299af06882bd4076586300da Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <[email protected]>
Date: Tue, 3 Feb 2026 20:29:54 +0200
Subject: [PATCH 2/2] Rearrange the checks in replorigin_session_setup()

Makes it easier to follow the logic.
---
 src/backend/replication/logical/origin.c | 73 +++++++++++++-----------
 1 file changed, 41 insertions(+), 32 deletions(-)

diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index ec040a039d3..cb18b4b6686 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -1185,55 +1185,64 @@ replorigin_session_setup(ReplOriginId node, int acquired_by)
 		if (curstate->roident != node)
 			continue;
 
-		else if (curstate->acquired_by != 0 && acquired_by == 0)
+		if (acquired_by == 0)
 		{
-			ereport(ERROR,
-					(errcode(ERRCODE_OBJECT_IN_USE),
-					 errmsg("replication origin with ID %d is already active for PID %d",
-							curstate->roident, curstate->acquired_by)));
+			/* With acquired_by == 0, we need the origin to be free */
+			if (curstate->acquired_by != 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_OBJECT_IN_USE),
+						 errmsg("replication origin with ID %d is already active for PID %d",
+								curstate->roident, curstate->acquired_by)));
+
+			if (curstate->refcount > 0)
+			{
+				/*
+				 * The origin is in use, but PID is not recorded. This can
+				 * happen if the process that originally acquired the origin
+				 * exited without releasing it. To ensure correctness, other
+				 * processes cannot acquire the origin until all processes
+				 * currently using it have released it.
+				 */
+				ereport(ERROR,
+						(errcode(ERRCODE_OBJECT_IN_USE),
+						 errmsg("replication origin with ID %d is already active in another process",
+								curstate->roident)));
+			}
 		}
-
-		else if (curstate->acquired_by != acquired_by)
+		else
 		{
-			ereport(ERROR,
-					(errcode(ERRCODE_OBJECT_IN_USE),
-					 errmsg("replication origin with ID %d is not active for PID %d",
-							curstate->roident, acquired_by)));
+			/*
+			 * With acquired_by != 0, we need the origin to be active by the
+			 * given PID
+			 */
+			if (curstate->acquired_by != acquired_by)
+				ereport(ERROR,
+						(errcode(ERRCODE_OBJECT_IN_USE),
+						 errmsg("replication origin with ID %d is not active for PID %d",
+								curstate->roident, acquired_by)));
 		}
 
-		/*
-		 * The origin is in use, but PID is not recorded. This can happen if
-		 * the process that originally acquired the origin exited without
-		 * releasing it. To ensure correctness, other processes cannot acquire
-		 * the origin until all processes currently using it have released it.
-		 */
-		else if (curstate->acquired_by == 0 && curstate->refcount > 0)
-			ereport(ERROR,
-					(errcode(ERRCODE_OBJECT_IN_USE),
-					 errmsg("replication origin with ID %d is already active in another process",
-							curstate->roident)));
-
 		/* ok, found slot */
 		session_replication_state = curstate;
 		break;
 	}
 
-
-	if (session_replication_state == NULL && free_slot == -1)
-		ereport(ERROR,
-				(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
-				 errmsg("could not find free replication state slot for replication origin with ID %d",
-						node),
-				 errhint("Increase \"max_active_replication_origins\" and try again.")));
-	else if (session_replication_state == NULL)
+	if (session_replication_state == NULL)
 	{
-		if (acquired_by)
+		if (acquired_by != 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 					 errmsg("cannot use PID %d for inactive replication origin with ID %d",
 							acquired_by, node)));
 
 		/* initialize new slot */
+		if (free_slot == -1)
+			ereport(ERROR,
+					(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+					 errmsg("could not find free replication state slot for replication origin with ID %d",
+							node),
+					 errhint("Increase \"max_active_replication_origins\" and try again.")));
+
 		session_replication_state = &replication_states[free_slot];
 		Assert(!XLogRecPtrIsValid(session_replication_state->remote_lsn));
 		Assert(!XLogRecPtrIsValid(session_replication_state->local_lsn));
-- 
2.47.3

Reply via email to