diff --git a/pcl_expect/remote_exception.py b/pcl_expect/remote_exception.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ad0cf343e9c29fa2ec5b9e1ca5c686816011b55
--- /dev/null
+++ b/pcl_expect/remote_exception.py
@@ -0,0 +1,96 @@
+"""Exception handling for remote processes.
+
+   If you have a controller process that spawns a child process, and
+   an unexpected exception occurs in the child process, you often want
+   to report it via the controller process.  Perhaps it can be handled
+   in the controller process.  At the very least, you want to get a
+   traceback from the child process.
+
+   This module defines a new exception, Remote, which encapsulates any
+   other exception (as long as the other exception only contains data
+   that can be cPickled).  It also provides two functions:
+
+    - serialize: call this in an exception handler in the child
+      process.  It will create a Remote exception based on
+      sys.exc_info(), serialize it, and return it as a string.
+
+    - re_raise: when given a string produced by serialize(), it will
+      raise the Remote exception.
+
+   This module does not attempt to transfer the serialized exception
+   from the child process to the controller process.  It is assumed
+   that you already have a channel where that can be done.
+
+   Typical usage:
+
+   In the client:
+
+       try:
+           # Code that can raise an exception
+       except:
+           x = remote_exception.serialize()
+           # Somehow send x to the controller.
+
+   In the server:
+
+       # Somehow receive the serialized string into x.
+       remote_exception.re_raise(x)
+
+   See the test code at the end of this module for working demo code.
+"""
+
+import sys
+import cPickle
+import traceback
+
+class Remote(Exception):
+    """An exception occured in a remote process.
+    
+       This exception defines these attributes:
+
+        - exc_type  -- The type of the original exception.
+        - exc_value -- The value of the original exception.
+        - traceback -- A preformatted traceback string, ready for
+                       printing (or writing to a log file, et c).
+    """
+
+    def __init__(self):
+        """Create a Remote exception.
+
+           The exception data will be gathered using sys.exc_info(),
+           so this must be created inside an except clause.
+        """
+        self.exc_type, self.exc_value, tb = sys.exc_info()
+        self.traceback = ''.join(traceback.format_exception(
+            self.exc_type, self.exc_value, tb))
+        Exception.__init__(self, self.exc_type.__name__, str(self.exc_value))
+
+def serialize():
+    """Create a Remote exception, serialize it, and return the string.
+
+       This must be called from within an except clause.
+
+    """
+
+    x = Remote()
+    return cPickle.dumps(x, cPickle.HIGHEST_PROTOCOL)
+
+def re_raise(s):
+    """Decode a serialized Remote exception, and raise it."""
+
+    raise cPickle.loads(s)
+
+# Demo code follows.
+if __name__ == "__main__":
+    try:
+        x
+    except:
+        y = serialize()
+
+    try:
+        re_raise(y)
+    except Remote, z:
+        print "Remote exception follows:"
+        print z.traceback
+        print "The exception was re-generated here:"
+        print ''.join(traceback.format_exception(*sys.exc_info()))