Fix `clipnorm` for TF models with `Embedding` layer(s).
diff --git a/keras/backend/tensorflow/optimizer.py b/keras/backend/tensorflow/optimizer.py
index 7ce1707b119..967c4e4d2a8 100644
--- a/keras/backend/tensorflow/optimizer.py
+++ b/keras/backend/tensorflow/optimizer.py
@@ -163,3 +163,8 @@ def _distributed_tf_increment_grad_acc(
             grads,
             accumulators,
         )
+
+    def _clip_by_norm(self, values, axes=None):
+        # We need to use TF-specific OP to support the case,
+        # when `values` are `tf.IndexedSlices`.
+        return tf.clip_by_norm(values, self.clipnorm, axes)
diff --git a/keras/optimizers/adam_test.py b/keras/optimizers/adam_test.py
index 4f33f4afd7f..1dcc876a1dd 100644
--- a/keras/optimizers/adam_test.py
+++ b/keras/optimizers/adam_test.py
@@ -84,3 +84,21 @@ def test_ema(self):
         x = keras.ops.zeros((1, 5))
         y = keras.ops.zeros((1, 10))
         model.fit(x, y)
+
+    @pytest.mark.skipif(
+        backend.backend() != "tensorflow",
+        reason="The IndexedSlices test can only run with TF backend.",
+    )
+    def test_clipnorm_indexed_slices(self):
+        # https://github.com/keras-team/keras/issues/18985
+        model = keras.Sequential(
+            [
+                keras.layers.Embedding(10, 4),
+                keras.layers.Flatten(),
+                keras.layers.Dense(2),
+            ]
+        )
+        model.compile(optimizer=Adam(clipnorm=100), loss="mse")
+        x = keras.ops.ones((8, 5))
+        y = keras.ops.zeros((8, 2))
+        model.fit(x, y, verbose=0)
diff --git a/keras/optimizers/base_optimizer.py b/keras/optimizers/base_optimizer.py
index f3cca634d1b..9757cae437b 100644
--- a/keras/optimizers/base_optimizer.py
+++ b/keras/optimizers/base_optimizer.py
@@ -589,7 +589,7 @@ def _clip_gradients(self, grads):
                 if g is None:
                     clipped_grads.append(g)
                 else:
-                    clipped_grads.append(clip_by_norm(g, self.clipnorm))
+                    clipped_grads.append(self._clip_by_norm(g))
             return clipped_grads
 
         if self.global_clipnorm and self.global_clipnorm > 0:
@@ -796,6 +796,19 @@ def __setattr__(self, name, value):
             value = self._tracker.track(value)
         return super().__setattr__(name, value)
 
+    def _clip_by_norm(self, values, axes=None):
+        # Calculate L2-norm, clip elements by ratio of clip_norm to L2-norm
+        l2sum = ops.sum(ops.square(values), axes, keepdims=True)
+        pred = l2sum > 0
+        # Two-tap tf.where trick to bypass NaN gradients
+        l2sum_safe = ops.where(pred, l2sum, ops.ones_like(l2sum))
+        l2norm = ops.where(pred, ops.sqrt(l2sum_safe), l2sum)
+        intermediate = ops.multiply(values, self.clipnorm)
+        values_clip = ops.convert_to_tensor(intermediate) / ops.maximum(
+            l2norm, self.clipnorm
+        )
+        return values_clip
+
 
 base_optimizer_keyword_args = """name: String. The name to use
             for momentum accumulator weights created by
@@ -845,20 +858,6 @@ def __setattr__(self, name, value):
 """
 
 
-def clip_by_norm(values, clip_norm, axes=None):
-    # Calculate L2-norm, clip elements by ratio of clip_norm to L2-norm
-    l2sum = ops.sum(values * values, axes, keepdims=True)
-    pred = l2sum > 0
-    # Two-tap tf.where trick to bypass NaN gradients
-    l2sum_safe = ops.where(pred, l2sum, ops.ones_like(l2sum))
-    l2norm = ops.where(pred, ops.sqrt(l2sum_safe), l2sum)
-    intermediate = values * clip_norm
-    values_clip = ops.convert_to_tensor(intermediate) / ops.maximum(
-        l2norm, clip_norm
-    )
-    return values_clip
-
-
 def global_norm(value_list):
     """Computes the global norm of multiple tensors."""
     squared_norms = []

Fix for the issue described in https://github.com/keras-team/keras/issues/18985