{"record":{"id":"4efdf6007166bde2","repo":"keras-team/keras","slug":"the-mask-passed-to-the-timedistributed-layer-m","errorCode":null,"errorMessage":"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}.","messagePattern":"The `mask` passed to the `TimeDistributed` layer must be at least 2D \\(e\\.g\\., `\\(batch_size, timesteps\\)`\\), but it has (.+?) dimension\\(s\\) with shape (.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"keras/src/layers/rnn/time_distributed.py","lineNumber":81,"sourceCode":"\n    def compute_output_shape(self, input_shape):\n        child_input_shape = self._get_child_input_shape(input_shape)\n        child_output_shape = self.layer.compute_output_shape(child_input_shape)\n        return (child_output_shape[0], input_shape[1], *child_output_shape[1:])\n\n    def build(self, input_shape):\n        child_input_shape = self._get_child_input_shape(input_shape)\n        super().build(child_input_shape)\n\n    def call(self, inputs, training=None, mask=None):\n        # Validate mask shape using static shape info when available\n        if mask is not None:\n            mask_shape = mask.shape\n            input_shape = inputs.shape\n\n            # Check if mask has at least 2 dimensions (batch and timesteps)\n            if len(mask_shape) < 2:\n                raise ValueError(\n                    \"The `mask` passed to the `TimeDistributed` layer must be \"\n                    \"at least 2D (e.g., `(batch_size, timesteps)`), but it has \"\n                    f\"{len(mask_shape)} dimension(s) with shape {mask_shape}.\"\n                )\n\n            # Check batch size and timesteps dimensions match\n            batch_mismatch = (\n                input_shape[0] is not None\n                and mask_shape[0] is not None\n                and input_shape[0] != mask_shape[0]\n            )\n            time_mismatch = (\n                input_shape[1] is not None\n                and mask_shape[1] is not None\n                and input_shape[1] != mask_shape[1]\n            )\n\n            if batch_mismatch or time_mismatch:","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/keras-team/keras/blob/7a34a03db60bf60042242d6a556fc3be119046a5/keras/src/layers/rnn/time_distributed.py#L63-L99","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)"],"exampleFix":"# before\nout = td_layer(x, mask=mask_1d)  # mask_1d shape (batch,)\n\n# after\nmask_2d = keras.ops.expand_dims(mask_1d, axis=-1)  # (batch, 1) timesteps axis\nout = td_layer(x, mask=mask_2d)","handlingStrategy":"validation","validationCode":"if mask is not None and len(mask.shape) < 2:\n    mask = keras.ops.expand_dims(mask, -1)  # ensure at least (batch, timesteps)\nout = td_layer(x, mask=mask)","typeGuard":"def is_valid_td_mask(mask) -> bool:\n    return mask is None or len(mask.shape) >= 2","tryCatchPattern":null,"preventionTips":["Let Keras propagate masks automatically via Embedding(mask_zero=True) instead of passing masks manually","Keep custom compute_mask outputs at >= 2D"],"tags":["keras","time-distributed","masking","shape-validation"],"backgroundTag":"mask-shape-mismatch","analyzedSha":"7a34a03db60bf60042242d6a556fc3be119046a5","analyzedAt":"2026-08-25T21:25:25.994Z","schemaVersion":2},"datasetVersion":"2026-08-26T02:17:13.382Z"}