diff -r 6d3bd7133374 django/contrib/gis/geos/geometry.py
--- a/django/contrib/gis/geos/geometry.py	Tue Nov 02 19:31:14 2010 +0000
+++ b/django/contrib/gis/geos/geometry.py	Tue Nov 02 15:00:21 2010 -0500
@@ -275,6 +275,15 @@
         "This property tests the validity of this Geometry."
         return capi.geos_isvalid(self.ptr)
 
+    @property
+    def invalid_reason(self):
+        """
+        Returns a string containing the reason for any invalidity.
+        """
+        if not GEOS_PREPARE:
+            raise GEOSException('Upgrade GEOS to 3.1 to get validity reason.')               
+        return capi.geos_isvalidreason(self.ptr)
+
     #### Binary predicates. ####
     def contains(self, other):
         "Returns true if other.within(this) returns true."
diff -r 6d3bd7133374 django/contrib/gis/geos/prototypes/__init__.py
--- a/django/contrib/gis/geos/prototypes/__init__.py	Tue Nov 02 19:31:14 2010 +0000
+++ b/django/contrib/gis/geos/prototypes/__init__.py	Tue Nov 02 15:00:21 2010 -0500
@@ -18,7 +18,7 @@
     to_hex, to_wkb, to_wkt
 
 # Miscellaneous routines.
-from django.contrib.gis.geos.prototypes.misc import geos_area, geos_distance, geos_length
+from django.contrib.gis.geos.prototypes.misc import *
 
 # Predicates
 from django.contrib.gis.geos.prototypes.predicates import geos_hasz, geos_isempty, \
diff -r 6d3bd7133374 django/contrib/gis/geos/prototypes/misc.py
--- a/django/contrib/gis/geos/prototypes/misc.py	Tue Nov 02 19:31:14 2010 +0000
+++ b/django/contrib/gis/geos/prototypes/misc.py	Tue Nov 02 15:00:21 2010 -0500
@@ -3,10 +3,13 @@
  ones that return the area, distance, and length.
 """
 from ctypes import c_int, c_double, POINTER
-from django.contrib.gis.geos.libgeos import GEOM_PTR
-from django.contrib.gis.geos.prototypes.errcheck import check_dbl
+from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOS_PREPARE
+from django.contrib.gis.geos.prototypes.errcheck import check_dbl, check_string
+from django.contrib.gis.geos.prototypes.geom import geos_char_p
 from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
 
+__all__ = ['geos_area', 'geos_distance', 'geos_length']
+
 ### ctypes generator function ###
 def dbl_from_geom(func, num_geom=1):
     """
@@ -26,3 +29,11 @@
 geos_area = dbl_from_geom(GEOSFunc('GEOSArea'))
 geos_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2)
 geos_length = dbl_from_geom(GEOSFunc('GEOSLength'))
+
+# Validity reason; only in GEOS 3.1+
+if GEOS_PREPARE:
+    geos_isvalidreason = GEOSFunc('GEOSisValidReason')
+    geos_isvalidreason.argtypes = [GEOM_PTR]
+    geos_isvalidreason.restype = geos_char_p
+    geos_isvalidreason.errcheck = check_string
+    __all__.append('geos_isvalidreason')
diff -r 6d3bd7133374 django/contrib/gis/geos/tests/test_geos.py
--- a/django/contrib/gis/geos/tests/test_geos.py	Tue Nov 02 19:31:14 2010 +0000
+++ b/django/contrib/gis/geos/tests/test_geos.py	Tue Nov 02 15:00:21 2010 -0500
@@ -1,6 +1,7 @@
 import ctypes, random, unittest, sys
 from django.contrib.gis.geos import *
 from django.contrib.gis.geos.base import gdal, numpy, GEOSBase
+from django.contrib.gis.geos.libgeos import GEOS_PREPARE
 from django.contrib.gis.geometry.test_data import TestDataMixin
 
 class GEOSTest(unittest.TestCase, TestDataMixin):
@@ -917,6 +918,26 @@
         for geom, merged in zip(ref_geoms, ref_merged):
             self.assertEqual(merged, geom.merged)
 
+    def test27_valid_reason(self):
+        "Testing IsValidReason support"
+        # Skipping tests if GEOS < v3.1.
+        if not GEOS_PREPARE: return
+
+        g = GEOSGeometry("POINT(0 0)")
+        self.assert_(g.valid)
+        self.assert_(isinstance(g.invalid_reason, basestring))
+        self.assertEqual(g.invalid_reason, "Valid Geometry")
+
+        print "\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n"
+
+        g = GEOSGeometry("LINESTRING(0 0, 0 0)")
+
+        self.assert_(not g.valid)
+        self.assert_(isinstance(g.invalid_reason, basestring))
+        self.assertEqual(g.invalid_reason, "Too few points in geometry component[0 0]")
+
+        print "\nEND - expecting GEOS_NOTICE; safe to ignore.\n"
+
 def suite():
     s = unittest.TestSuite()
     s.addTest(unittest.makeSuite(GEOSTest))
diff -r 6d3bd7133374 docs/ref/contrib/gis/geos.txt
--- a/docs/ref/contrib/gis/geos.txt	Tue Nov 02 19:31:14 2010 +0000
+++ b/docs/ref/contrib/gis/geos.txt	Tue Nov 02 15:00:21 2010 -0500
@@ -219,6 +219,12 @@
 
 Returns a boolean indicating whether the geometry is valid.
 
+.. attribute:: GEOSGeometry.invalid_reason 
+
+.. versionadded:: 1.3 
+
+ Returns a string describing the reason why a geometry is invalid. 
+
 .. attribute:: GEOSGeometry.srid
 
 Property that may be used to retrieve or set the SRID associated with the
