diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out
index 9af7ea7292..33b64a0604 100644
--- a/src/pl/plpython/expected/plpython_error.out
+++ b/src/pl/plpython/expected/plpython_error.out
@@ -445,3 +445,17 @@ PL/Python function "notice_outerfunc"
                 1
 (1 row)
 
+/* When an error logged with an underlying exception, the detail should
+*  contain a detail message from the exception
+*/
+CREATE FUNCTION python_error_detail() RETURNS SETOF text AS $$
+  plan = plpy.prepare("SELECT to_date('DD', 'DD') d")
+  for row in plpy.cursor(plan):
+    yield row['d']
+$$ LANGUAGE plpython3u;
+SELECT python_error_detail();
+ERROR:  error fetching next item from iterator
+DETAIL:  spiexceptions.InvalidDatetimeFormat: invalid value "DD" for "DD"
+Value must be an integer.
+CONTEXT:  Traceback (most recent call last):
+PL/Python function "python_error_detail"
diff --git a/src/pl/plpython/expected/plpython_error_5.out b/src/pl/plpython/expected/plpython_error_5.out
index 7fe864a1a5..9cd0c66c55 100644
--- a/src/pl/plpython/expected/plpython_error_5.out
+++ b/src/pl/plpython/expected/plpython_error_5.out
@@ -445,3 +445,17 @@ PL/Python function "notice_outerfunc"
                 1
 (1 row)
 
+/* When an error logged with an underlying exception, the detail should
+*  contain a detail message from the exception
+*/
+CREATE FUNCTION python_error_detail() RETURNS SETOF text AS $$
+  plan = plpy.prepare("SELECT to_date('DD', 'DD') d")
+  for row in plpy.cursor(plan):
+    yield row['d']
+$$ LANGUAGE plpython3u;
+SELECT python_error_detail();
+ERROR:  error fetching next item from iterator
+DETAIL:  spiexceptions.InvalidDatetimeFormat: invalid value "DD" for "DD"
+Value must be an integer.
+CONTEXT:  Traceback (most recent call last):
+PL/Python function "python_error_detail"
diff --git a/src/pl/plpython/plpy_elog.c b/src/pl/plpython/plpy_elog.c
index 7c627eacfb..c88490bf50 100644
--- a/src/pl/plpython/plpy_elog.c
+++ b/src/pl/plpython/plpy_elog.c
@@ -47,6 +47,7 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 	char	   *tbmsg;
 	int			tb_depth;
 	StringInfoData emsg;
+	StringInfoData detailstr;
 	PyObject   *exc,
 			   *val,
 			   *tb;
@@ -62,6 +63,8 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 	char	   *datatype_name = NULL;
 	char	   *constraint_name = NULL;
 
+	detailstr.data = NULL;
+
 	PyErr_Fetch(&exc, &val, &tb);
 
 	if (exc != NULL)
@@ -103,12 +106,19 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 		}
 		primary = emsg.data;
 
-		/* Since we have a format string, we cannot have a SPI detail. */
-		Assert(detail == NULL);
-
-		/* If there's an exception message, it goes in the detail. */
-		if (xmsg)
+		/*
+		 * If there's an exception message, it goes to the detail,
+		 * prepended to the existing detail message, if any.
+		 */
+		if (detail == NULL)
 			detail = xmsg;
+		else if (xmsg != NULL) {
+			initStringInfo(&detailstr);
+			appendStringInfoString(&detailstr, xmsg);
+			appendStringInfoChar(&detailstr, '\n');
+			appendStringInfoString(&detailstr, detail);
+			detail = detailstr.data;
+		}
 	}
 	else
 	{
@@ -141,6 +151,8 @@ PLy_elog_impl(int elevel, const char *fmt,...)
 	{
 		if (fmt)
 			pfree(emsg.data);
+		if (detailstr.data)
+			pfree(detailstr.data);
 		if (xmsg)
 			pfree(xmsg);
 		if (tbmsg)
diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql
index 11f14ec5a7..554eb350b5 100644
--- a/src/pl/plpython/sql/plpython_error.sql
+++ b/src/pl/plpython/sql/plpython_error.sql
@@ -344,3 +344,14 @@ $$ LANGUAGE plpython3u;
 \set SHOW_CONTEXT always
 
 SELECT notice_outerfunc();
+
+/* When an error logged with an underlying exception, the detail should
+*  contain a detail message from the exception
+*/
+CREATE FUNCTION python_error_detail() RETURNS SETOF text AS $$
+  plan = plpy.prepare("SELECT to_date('DD', 'DD') d")
+  for row in plpy.cursor(plan):
+    yield row['d']
+$$ LANGUAGE plpython3u;
+
+SELECT python_error_detail();
