         in f: @differentiable (Self, T) -> Tensor<R>
     ) -> (value: Tensor<R>, gradient: (TangentVector, T.TangentVector)) {
         let (y, pb) = self.valueWithPullback(at: x, in: f)
-        precondition(y.rank == 0)
+        precondition(y.rank == 0, """
+            The function being differentiated produced a tensor with shape \(y.shape). \
+            You can only compute the gradient of functions that return scalar values.
+            """)
         return (y, pb(Tensor<R>(1)))
     }
 }

) -> (value: Tensor<R>, gradient: (T.TangentVector, U.TangentVector))
    where T: Differentiable, U: Differentiable, R: TensorFlowFloatingPoint {
    let (y, pullback) = valueWithPullback(at: x, y, in: f)
    precondition(y.rank == 0)
    return (y, pullback(Tensor<R>(1)))
}


        in f: @differentiable (Self) -> Tensor<R>
    ) -> (value: Tensor<R>, gradient: TangentVector) {
        let (y, pb) = self.valueWithPullback(in: f)
        precondition(y.rank == 0, """
            The function being differentiated produced a tensor with shape \(y.shape). \
            You can only compute the gradient of functions that return scalar values.
            """)
        return (y, pb(Tensor<R>(1)))
    }

