It appears that lxd/client/lxd_instances.go:ExecInstance() does not wait for the typical WS close frame, this makes me assume that the server isn’t sending one either.
That may well be the problem as the python websockets library will wait for the close frame, this doesn’t explain why the delayed exit doesn’t always happen.
Setting close_timeout=0 in the websockets.connect() call solved this problem. Hooray. Someone should try to fix this in PyLXD