You’re running exactly into the lack of synchronization I alluded to in my original answer:
You create bobTV
on Alice’s participant but then you try to use it on Bob’s too early and it fails. The reason why it works on the second try is probably due to JIT warmup which makes it sufficiently fast that it works (most of the time at least).
One option for synchronizing is to poll until the contract is visible:
waitForCid : Template t => Party -> ContractId t -> Script ()
waitForCid p cid = do
r <- queryContractId p cid
case r of
None -> do
sleep (seconds 1)
waitForCid p cid
Some _ -> pure ()
setup : Script AssetId
setup = script do
alice <- allocatePartyOn "Alice" (ParticipantName "p1")
bob <- allocatePartyOn "Bob" (ParticipantName "p2")
aliceTV <- submit alice do
createCmd Asset with
issuer = alice
owner = alice
name = "TV"
bobTV <- submit alice do
exerciseCmd aliceTV Give with newOwner = bob
waitForCid bob bobTV
submit bob do
exerciseCmd bobTV Give with newOwner = alice
With that change it works on the first try for me as well.