This looks tricky to achieve with multiple optimizers.
I am not sure if this is the reason. But you are calling multiple optimizer.step()'s in between and the weights of the encoder, decoder are modified in-place due to that. What if you try the weight updates in reverse order (discriminator, then decoder, then encoder):
calculate encoder loss, decoder loss, discriminator loss

# discriminator update
optimizer_disc.zero_grad()
lossD.backward(retain_graph=True)
optimizer_disc.step()

# decoder update
optimizer_decoder.zero_grad()
loss_decoder.backward(retain_graph=True)
optimizer_decoder.step()

# encoder update
optimizer_encoder.zero_grad()
loss_ele.backward(retain_graph=True)
optimizer_encoder.step()

# to release the computation graph
lossD.backward()