To enable read only commit optimizations, shard responses to transaction commands contain a readOnly boolean field that is true if the shard's local transaction is "in-progress" and has applied no operations that would be written to the oplog. While running a sharded transaction, mongos tracks which shards have ever returned readOnly=false and will skip two phase commit if no participants ever did, sending commitTransaction directly to each shard instead.
After committing however, a shard will fail the in-progress condition, so its commit response contains readOnly=false, leading the router to mark the participant as not read only. This means if the client retries commit against the original mongos, the router will no longer believe the transaction is read only, so it sends coordinateCommit to the coordinator, which will try to drive a two phase commit. This will fail during phase one if any participants already committed the txnId, because the coordinator receives TransactionCommitted as their prepare response and retries sending prepare until timing out.
This is especially a problem for failover testing, because a stepdown on any shard during read only commit will lead the shell to retry commitTransaction (because it is treated as a retryable write), which stalls until the coordinator times out if any other shard successfully committed.