diff --git a/django/contrib/gis/geos/libgeos.py b/django/contrib/gis/geos/libgeos.py
index feb225cf8c..7a5b11bdd0 100644
--- a/django/contrib/gis/geos/libgeos.py
+++ b/django/contrib/gis/geos/libgeos.py
@@ -97,7 +97,8 @@ def error_h(fmt, lst):
         err_msg = fmt % lst
     except TypeError:
         err_msg = fmt
-    logger.error("GEOS_ERROR: %s\n", err_msg)
+    from django.contrib.gis.geos.prototypes.threadsafe import thread_context
+    thread_context.last_error = err_msg
 
 
 error_h = ERRORFUNC(error_h)
diff --git a/django/contrib/gis/geos/prototypes/errcheck.py b/django/contrib/gis/geos/prototypes/errcheck.py
index 044bf8bc5c..18839983a9 100644
--- a/django/contrib/gis/geos/prototypes/errcheck.py
+++ b/django/contrib/gis/geos/prototypes/errcheck.py
@@ -6,6 +6,7 @@ from ctypes import c_void_p, string_at
 
 from django.contrib.gis.geos.error import GEOSException
 from django.contrib.gis.geos.libgeos import GEOSFuncFactory
+from django.contrib.gis.geos.prototypes.threadsafe import thread_context
 
 # Getting the `free` routine used to free the memory allocated for
 # string pointers returned by GEOS.
@@ -24,59 +25,84 @@ def check_dbl(result, func, cargs):
     """
     # Checking the status code
     if result != 1:
+        thread_context.last_error = None
         return None
     # Double passed in by reference, return its value.
+    thread_context.last_error = None
     return last_arg_byref(cargs)
 
 
 def check_geom(result, func, cargs):
     "Error checking on routines that return Geometries."
     if not result:
-        raise GEOSException(
-            'Error encountered checking Geometry returned from GEOS C function "%s".'
-            % func.__name__
+        geos_msg = thread_context.last_error
+        thread_context.last_error = None
+
+        error_msg = (
+            f'Error encountered checking Geometry returned from GEOS C function '
+            f'"{func.__name__}".'
         )
+        if geos_msg:
+            error_msg = f"{error_msg} {geos_msg}"
+        raise GEOSException(error_msg)
+    thread_context.last_error = None
     return result
 
 
 def check_minus_one(result, func, cargs):
     "Error checking on routines that should not return -1."
     if result == -1:
-        raise GEOSException(
-            'Error encountered in GEOS C function "%s".' % func.__name__
-        )
+        geos_msg = thread_context.last_error
+        thread_context.last_error = None
+
+        error_msg = f'Error encountered in GEOS C function "{func.__name__}".'
+        if geos_msg:
+            error_msg = f"{error_msg} {geos_msg}"
+        raise GEOSException(error_msg)
     else:
+        thread_context.last_error = None
         return result
 
 
 def check_predicate(result, func, cargs):
     "Error checking for unary/binary predicate functions."
     if result == 1:
+        thread_context.last_error = None
         return True
     elif result == 0:
+        thread_context.last_error = None
         return False
     else:
-        raise GEOSException(
-            'Error encountered on GEOS C predicate function "%s".' % func.__name__
-        )
+        geos_msg = thread_context.last_error
+        thread_context.last_error = None
+
+        error_msg = f'Error encountered on GEOS C predicate function "{func.__name__}".'
+        if geos_msg:
+            error_msg = f"{error_msg} {geos_msg}"
+        raise GEOSException(error_msg)
 
 
 def check_sized_string(result, func, cargs):
     """
     Error checking for routines that return explicitly sized strings.
 
     This frees the memory allocated by GEOS at the result pointer.
     """
     if not result:
-        raise GEOSException(
-            'Invalid string pointer returned by GEOS C function "%s"' % func.__name__
-        )
+        geos_msg = thread_context.last_error
+        thread_context.last_error = None
+
+        error_msg = f'Invalid string pointer returned by GEOS C function "{func.__name__}"'
+        if geos_msg:
+            error_msg = f"{error_msg} {geos_msg}"
+        raise GEOSException(error_msg)
     # A c_size_t object is passed in by reference for the second
     # argument on these routines, and its needed to determine the
     # correct size.
     s = string_at(result, last_arg_byref(cargs))
     # Freeing the memory allocated within GEOS
     free(result)
+    thread_context.last_error = None
     return s
 
 
@@ -87,12 +113,19 @@ def check_string(result, func, cargs):
     This frees the memory allocated by GEOS at the result pointer.
     """
     if not result:
-        raise GEOSException(
-            'Error encountered checking string return value in GEOS C function "%s".'
-            % func.__name__
+        geos_msg = thread_context.last_error
+        thread_context.last_error = None
+
+        error_msg = (
+            f'Error encountered checking string return value in GEOS C function '
+            f'"{func.__name__}".'
         )
+        if geos_msg:
+            error_msg = f"{error_msg} {geos_msg}"
+        raise GEOSException(error_msg)
     # Getting the string value at the pointer address.
     s = string_at(result)
     # Freeing the memory allocated within GEOS
     free(result)
+    thread_context.last_error = None
     return s
diff --git a/django/contrib/gis/geos/prototypes/threadsafe.py b/django/contrib/gis/geos/prototypes/threadsafe.py
index d4f7ffb8ac..eedfc3f32a 100644
--- a/django/contrib/gis/geos/prototypes/threadsafe.py
+++ b/django/contrib/gis/geos/prototypes/threadsafe.py
@@ -20,6 +20,7 @@ class GEOSContextHandle(GEOSBase):
 # to hold a reference to GEOSContextHandle for this thread.
 class GEOSContext(threading.local):
     handle = None
+    last_error = None
 
 
 thread_context = GEOSContext()
diff --git a/tests/gis_tests/data/geometries.json b/tests/gis_tests/data/geometries.json
index 6856ac793a..a66ab14843 100644
--- a/tests/gis_tests/data/geometries.json
+++ b/tests/gis_tests/data/geometries.json
@@ -13,9 +13,9 @@
   "errors": [
     {"wkt": "GEOMETR##!@#%#............a32515", "bad": true, "hex": false, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."},
     {"wkt": "Foo.Bar", "bad": true, "hex": false, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."},
-    {"wkt": "POINT (5, 23)", "bad": true, "hex": false, "msg": "Error encountered checking Geometry returned from GEOS C function \"GEOSWKTReader_read_r\"."},
+    {"wkt": "POINT (5, 23)", "bad": true, "hex": false, "msg": "Error encountered checking Geometry returned from GEOS C function \"GEOSWKTReader_read_r\". ParseException: Expected number but encountered ','"},
     {"wkt": "AAABBBDDDAAD##@#1113511111-098111111111111111533333333333333", "bad": true, "hex": true, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."},
-    {"wkt": "FFFFFFFFFFFFFFFFF1355555555555555555565111", "bad": true, "hex": true, "msg": "Error encountered checking Geometry returned from GEOS C function \"GEOSWKBReader_readHEX_r\"."},
+    {"wkt": "FFFFFFFFFFFFFFFFF1355555555555555555565111", "bad": true, "hex": true, "msg": "Error encountered checking Geometry returned from GEOS C function \"GEOSWKBReader_readHEX_r\". ParseException: Unknown WKB type 535"},
     {"wkt": "", "bad": true, "hex": false, "msg": "String input unrecognized as WKT EWKT, and HEXEWKB."}
   ],
   "wkt_out": [
