When I try to serialize an exception using json.dump, I get errors like

TypeError: IOError('socket error', error(61, 'Connection refused')) is not JSON serializable

and

TypeError: error(61, 'Connection refused') is not JSON serializable

The __dict__ field of exceptions is {} (this is why How to make a class JSON serializable does not help me: the answers there assume that __dict__ contains all the necessary information, they also assume that I have control over the class to be serialized).

Is there something more intelligent that saving str(exn)?

I would prefer a human-readable text representation (not pickle).

PS. Here is what I came up with:

def exception_as_dict(ex):
    return dict(type=ex.__class__.__name__,
                errno=ex.errno, message=ex.message,
                strerror=exception_as_dict(ex.strerror)
                if isinstance(ex.strerror,Exception) else ex.strerror)

json.dumps(exception_as_dict(err),indent=2)
{
  "errno": "socket error", 
  "type": "IOError", 
  "strerror": {
    "errno": 61, 
    "type": "error", 
    "strerror": "Connection refused"
  }
}

Exceptions can not be pickled (by default), you have two options:

  1. Use Python's built in format_exc() and serialize the formatted string.

  2. Use tblib

With the latter, you can pass wrapped exceptions and also reraise them later.

import tblib.pickling_support
tblib.pickling_support.install()
import pickle, sys 

def inner_0():
    raise Exception('fail')

def inner_1():
    inner_0()

def inner_2():
    inner_1()

try:
    inner_2()
except:
    s1 = pickle.dumps(sys.exc_info())

You can use exc_info with traceback as below:

import traceback
import sys
try:
    raise KeyError('aaa!!!')
except Exception as e:
    exc_info = sys.exc_info()
    print(''.join(traceback.format_exception(*exc_info)))