As pointed out in pion/webrtc#1212 (comment) , Pion's handling of the failed state is somewhat naive: Pion will switch to failed if connectivity cannot be established even if it hasn't received an end-of-candidates indication.
There's a tradeoff here: the current behaviour works well enough in practice, and it gracefully handles the case when the peer never sends an end-of-candidates indication. On the other hand, it means that if the peer is performing expensive gathering (e.g. because a TURN server is overloaded), we might spuriously switch into failed.