keras-team/keras · error · ValueError
The `mask` passed to the `TimeDistributed` layer must be at
Error message
The `mask` passed to the `TimeDistributed` layer must be at least 2D (e.g., `(batch_size, timesteps)`), but it has {len(mask_shape)} dimension(s) with shape {mask_shape}. What it means
When a mask is passed to TimeDistributed.call, the mask must carry per-(batch, timestep) validity, so it must be at least 2D with shape (batch_size, timesteps). A 0D or 1D mask cannot be aligned with the time axis, so the layer rejects it before checking dimension matches. This typically surfaces when an upstream masking layer (e.g. Embedding(mask_zero=True) or Masking) produced a degenerate mask.
Source
Thrown at keras/src/layers/rnn/time_distributed.py:81
def compute_output_shape(self, input_shape):
child_input_shape = self._get_child_input_shape(input_shape)
child_output_shape = self.layer.compute_output_shape(child_input_shape)
return (child_output_shape[0], input_shape[1], *child_output_shape[1:])
def build(self, input_shape):
child_input_shape = self._get_child_input_shape(input_shape)
super().build(child_input_shape)
def call(self, inputs, training=None, mask=None):
# Validate mask shape using static shape info when available
if mask is not None:
mask_shape = mask.shape
input_shape = inputs.shape
# Check if mask has at least 2 dimensions (batch and timesteps)
if len(mask_shape) < 2:
raise ValueError(
"The `mask` passed to the `TimeDistributed` layer must be "
"at least 2D (e.g., `(batch_size, timesteps)`), but it has "
f"{len(mask_shape)} dimension(s) with shape {mask_shape}."
)
# Check batch size and timesteps dimensions match
batch_mismatch = (
input_shape[0] is not None
and mask_shape[0] is not None
and input_shape[0] != mask_shape[0]
)
time_mismatch = (
input_shape[1] is not None
and mask_shape[1] is not None
and input_shape[1] != mask_shape[1]
)
if batch_mismatch or time_mismatch:View on GitHub (pinned to 7a34a03db6)
Solutions
- Ensure the mask has shape (batch_size, timesteps) matching the input's first two dims
- Fix the upstream layer's compute_mask to not squeeze below 2D
- If calling manually, expand the mask: mask = keras.ops.expand_dims(mask, axis=-1) so it becomes (batch, 1) or reshape to (batch, timesteps)
Example fix
# before out = td_layer(x, mask=mask_1d) # mask_1d shape (batch,) # after mask_2d = keras.ops.expand_dims(mask_1d, axis=-1) # (batch, 1) timesteps axis out = td_layer(x, mask=mask_2d)
Defensive patterns
Strategy: validation
Validate before calling
if mask is not None and len(mask.shape) < 2:
mask = keras.ops.expand_dims(mask, -1) # ensure at least (batch, timesteps)
out = td_layer(x, mask=mask) Type guard
def is_valid_td_mask(mask) -> bool:
return mask is None or len(mask.shape) >= 2 Prevention
- Let Keras propagate masks automatically via Embedding(mask_zero=True) instead of passing masks manually
- Keep custom compute_mask outputs at >= 2D
When it happens
Trigger: Passing mask with shape (), (batch,), or (batch*timesteps,) to TimeDistributed; a custom upstream layer's compute_mask returning a 1D tensor; manually calling layer(x, mask=flat_mask).
Common situations: A preceding Masking/Embedding(mask_zero=True) layer emitting a squeezed mask; custom layers whose compute_mask calls ops.squeeze; Keras 3 migrations where legacy mask plumbing differed; ragged data converted incorrectly to dense tensors.
Related errors
- `TimeDistributed` Layer should be passed an `input_shape` wi
- The `mask` passed to the `TimeDistributed` layer has a shape
- The `weights` argument should be either `None` (random initi
- Expected mode to be one of `caffe`, `tf` or `torch`. Receive
- `mask` should be a list. Received: mask={mask}
AI-assisted analysis of keras-team/keras@7a34a03db6 (2026-08-25).
Data as JSON: /api/errors/4efdf6007166bde2.
Report an issue: GitHub.