I don't know about the proposed solution (the
onerror part of the question). This answer deals differently with the original problem.
At the very least, the original action can maintain a flag in the RAM, and the deferred one can clear that flag upon completion.
It might naturally be more than just a flag, depending on what you want it to do.
A practical example is
eosio.system with its name bidding feature.
A bid event should return EOS to the previous bidder.
Instead of just sending EOS (which would be a vulnerability), it makes a record in the
refunds_table, and then tries to send a deferred transaction with
bidrefund action transfers the EOS and clears the
In case the deferred transaction fails for any reason, the
bidrefund action can be called again anytime, without the
eosio.system contract's permission.
The code can be found here.