OK, I think I might have figured it out – each param.grad is either not volatile (if the backward pass that created it was made entirely of double-backprop-ready functions) or volatile (if the backward pass that created it included some functions that aren’t double-backprop-ready). What you want is actually for k and g to be neither volatile=False (because that would lead PyTorch to try to backpropagate through them) nor volatile=True (because that flag is greedy and would lead the whole network to stop building a graph entirely). Instead you should set the variables in both k and g to be volatile=False, requires_grad=False. You can either do this by explicitly setting the flags or by creating k and g as:
g = [Variable(param.grad.data) for param in model.parameters()]

since volatile=False, requires_grad=False is the default for new Variables.
The latter would exactly replicate what’s going on in Chainer, since Chainer’s param.grad is a tensor rather than a variable and Chainer autowraps tensors into Variables.