TensorFlow
tf.tape.gradient
debugging
loss function
machine learning

tf.tape.gradient returns None for certain losses

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

When tf.GradientTape.gradient() returns None, TensorFlow is telling you that it could not trace a differentiable path from the loss back to the variable you asked about. This usually means the variable was not watched, the computation graph was broken, or the loss used an operation that is not differentiable with respect to that variable.

Core Sections

A Working Example First

Here is the normal pattern:

python
1import tensorflow as tf
2
3w = tf.Variable(3.0)
4
5with tf.GradientTape() as tape:
6    loss = (w - 10.0) ** 2
7
8grad = tape.gradient(loss, w)
9print(grad.numpy())

Because loss clearly depends on w, TensorFlow can compute the derivative.

Cause 1: The Loss Does Not Depend on the Variable

If the variable does not actually influence the loss, the gradient is unconnected and TensorFlow returns None.

python
1import tensorflow as tf
2
3w = tf.Variable(3.0)
4
5with tf.GradientTape() as tape:
6    loss = tf.constant(5.0)
7
8grad = tape.gradient(loss, w)
9print(grad)

This is mathematically correct. If the loss is constant with respect to w, there is no gradient to compute.

This also happens in more subtle ways, such as computing a loss from a tensor that was detached earlier or using the wrong model output by mistake.

Cause 2: You Left the TensorFlow Graph

One of the most common reasons for None is converting tensors to NumPy or Python values inside the tape block. Once you do that, TensorFlow can no longer track the operations that follow.

python
1import tensorflow as tf
2
3w = tf.Variable(3.0)
4
5with tf.GradientTape() as tape:
6    y = w * 2.0
7    loss = y.numpy() ** 2
8
9grad = tape.gradient(loss, w)
10print(grad)

The fix is simple: keep the loss computation in TensorFlow ops.

python
with tf.GradientTape() as tape:
    y = w * 2.0
    loss = tf.square(y)

The same warning applies to Python math functions, list conversions, and custom logic that extracts raw values too early.

Cause 3: The Tensor Is Not Being Watched

Trainable tf.Variable objects are watched automatically, but plain tensors are not. If you want gradients with respect to a tensor created by tf.constant or another non-variable source, call tape.watch.

python
1import tensorflow as tf
2
3x = tf.constant(3.0)
4
5with tf.GradientTape() as tape:
6    tape.watch(x)
7    loss = x ** 2
8
9grad = tape.gradient(loss, x)
10print(grad.numpy())

Without tape.watch(x), the result would be None.

Cause 4: Non-Differentiable Operations

Some operations do not have useful gradients, especially discrete ones such as argmax, integer indexing decisions, or explicit rounding.

python
1import tensorflow as tf
2
3w = tf.Variable([0.1, 0.9, 0.2], dtype=tf.float32)
4
5with tf.GradientTape() as tape:
6    index = tf.argmax(w)
7    loss = tf.cast(index, tf.float32)
8
9grad = tape.gradient(loss, w)
10print(grad)

argmax chooses a discrete position, so there is no meaningful gradient back to the original vector in the usual sense. If your model logic depends on such steps, you may need a differentiable approximation or a different training formulation.

Debugging Strategy

When gradients come back as None, walk backward from the loss:

  • confirm the loss uses TensorFlow ops only
  • confirm the loss depends on the variable
  • confirm the variable is a watched tf.Variable or explicitly watched tensor
  • check for non-differentiable operations

For multiple variables:

python
grads = tape.gradient(loss, model.trainable_variables)
for variable, grad in zip(model.trainable_variables, grads):
    print(variable.name, grad is None)

That quickly shows which path is broken.

Common Pitfalls

  • Calling .numpy() or using Python-only math inside the tape block and breaking the gradient path.
  • Building a loss that does not actually depend on the variable you are differentiating with respect to.
  • Forgetting to watch a plain tensor that is not a trainable tf.Variable.
  • Introducing non-differentiable ops such as argmax, rounding, or integer casts into the loss path.
  • Confusing an unconnected gradient of None with a valid gradient whose numeric value is zero.

Summary

  • 'None means TensorFlow found no differentiable path from the loss to the target variable.'
  • The loss must actually depend on the variable you differentiate with respect to.
  • Avoid leaving TensorFlow space with .numpy() or Python-only math inside the tape block.
  • Use tape.watch(...) for tensors that are not trainable tf.Variable objects.
  • Distinguish between an unconnected gradient of None and a valid gradient whose numeric value is zero.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.