Thanks for your reply.
Though optimizer2 is constructed with parameters of model2, which includes an encoder and a decoder, loss2 is computed with forward pass of input1 and input2 with encode_only, which are respectively model1_output and model2_output; decoder is not involved in the forward pass.
So yes, neither model1_output and model2_output are created by the decoder. The decoder was only involved in training model1 (i.e., task1, but I am focusing on task2 in this post).
Therefore, what happens is as you said: "loss2.backward() has no way of finding the decoder and updating any gradients", thereby optimizer2.step() doesn’t update the weights of the decoder, correct?
I will play around .eval() and detach(). If you are correct, loss2.backward()  and optimizer2.step() will update the gradients and weights of the encoder in the loaded, trained model1, which is not desired.