You get that behaviour both in shared and exclusive/private (global lock on candidate) config mode.
There is a read lock on the candidate when you commit the candidate, so you enter a deadlock when you do a maapi read (here maapi_save_config) of the candidate from your prepare phase config change subscriber.
The running config is not empty since the prepare phase comes after write phase. See ConfD UG 6.2 Chapter 7.5. User sessions and ConfD Transactions for an overview.
As a subscriber, you can start a session towards both CDB_RUNNING and CDB_PRE_COMIT_RUNNING. See “cdb_start_session()” description in the confd_lib_cdb man page.
If you can describe why read need to log the candidate configuration as XML at the prepare phase we may be able to suggest alternative solutions.
If you want to log the candidate for audit reasons a solution could be to just log the diff from your subscriber:
See also ConfD UG chapter 12.2. Audit Messages and 12.4. Commit Events and examples.confd/misc/notifications for other audit log alternatives.