Thinking about this some, I may have never called jointcal with more than one tract at a time, and I suspect Hsin-Fang Chiang does the same (specifying one tract plus its visit list per run). Which is why I hadn't encountered this before.
That said, I suspect the problem is that JointcalRunner calls kwargs.pop('butler'), which removes "butler" from the kwargs dict. I'm not sure why I made that pop instead of just butler=kwargs['butler']: I probably cargo cult-ed some other file. I'm betting that the kwargs list gets reused in whatever calls jointcalRunner(), so the butler is lost for the second call.
I've pushed what might be a trivial fix to this. Dominique Boutigny: does this ticket fix the issue for you? Given that JointcalRunner is going to be replaced in butler gen3, I'm not sure its worth spending time developing a unittest for this.