keras-team/keras · error · ValueError

The `mask` passed to the `TimeDistributed` layer has a shape

Error message

The `mask` passed to the `TimeDistributed` layer has a shape {mask_shape} that is incompatible with the input shape {input_shape}. The first two dimensions of the mask (batch size and timesteps) must match the input's first two dimensions. Expected mask shape prefix: ({input_shape[0]}, {input_shape[1]}).

What it means

After confirming the mask is at least 2D, TimeDistributed verifies that the mask's first two dimensions (batch and timesteps) equal the input's first two dimensions whenever both are known. A differently-sized mask would be broadcast against the wrong timesteps and silently zero out valid steps, so the layer raises instead.

Source

Thrown at keras/src/layers/rnn/time_distributed.py:100

                    "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:
                raise ValueError(
                    "The `mask` passed to the `TimeDistributed` layer has a "
                    f"shape {mask_shape} that is incompatible with the input "
                    f"shape {input_shape}. The first two dimensions of the "
                    "mask (batch size and timesteps) must match the input's "
                    "first two dimensions. Expected mask shape prefix: "
                    f"({input_shape[0]}, {input_shape[1]})."
                )

        input_shape = ops.shape(inputs)

        def time_distributed_transpose(data):
            """Swaps the timestep and batch dimensions of a tensor."""
            axes = [1, 0, *range(2, len(data.shape))]
            return ops.transpose(data, axes=axes)

        inputs = time_distributed_transpose(inputs)
        if mask is not None:
            mask = time_distributed_transpose(mask)

View on GitHub (pinned to 7a34a03db6)

Solutions

  1. Regenerate the mask from the same input (e.g. keep Embedding(mask_zero=True) immediately upstream so Keras propagates the correct mask)
  2. Make sure sequence padding maxlen matches the mask's timestep dimension
  3. Compare input.shape[:2] vs mask.shape[:2] before calling and reshape or recompute if unequal

Example fix

# before
mask = old_mask  # shape (32, 50)
out = td_layer(x, mask=mask)  # x shape (32, 100)

# after
# let Keras propagate the mask automatically
model = keras.Sequential([keras.layers.Embedding(vocab, dim, mask_zero=True), td_layer])
out = model(x)
Defensive patterns

Strategy: validation

Validate before calling

if mask is not None:
    ins, msk = tuple(inputs.shape[:2]), tuple(mask.shape[:2])
    assert ins == msk or None in ins + msk, f'mask {msk} != input {ins}'
out = td_layer(inputs, mask=mask)

Type guard

def mask_matches_input(mask, x) -> bool:
    return tuple(mask.shape[:2]) == tuple(x.shape[:2])

Prevention

When it happens

Trigger: Passing a mask of shape (batch, other_timesteps) or (other_batch, timesteps) to TimeDistributed; reusing a cached mask from a differently-batched input; a custom compute_mask that hardcodes or slices timesteps incorrectly.

Common situations: Padding/truncating sequences to a different maxlen than the mask was built with; splitting batches but reusing the old mask; pipelines that cache masks; stateful RNN reuse across sequences of different lengths.

Related errors


AI-assisted analysis of keras-team/keras@7a34a03db6 (2026-08-25). Data as JSON: /api/errors/a6a75afef3c7c341. Report an issue: GitHub.