keras-team/keras · error · ValueError

Argument `non_trainable_variables` must be a list of tensors

Error message

Argument `non_trainable_variables` must be a list of tensors corresponding 1:1 to {self.__class__.__name__}().non_trainable_variables. Received list with length {len(non_trainable_variables)}, but expected {len(self.non_trainable_variables)} variables.

What it means

stateless_call() also validates the non_trainable_variables list length against the layer's own non-trainable variables (BatchNorm moving statistics, quantization scale/zero-point, etc.). A mismatch raises this error before any computation.

Source

Thrown at keras/src/layers/layer.py:1159

        ```
        """
        self._check_super_called()
        if not self.built:
            raise ValueError(
                f"To call stateless_call, {self.__class__.__name__} must be "
                "built (i.e. its variables must have been already created). "
                "You can build it by calling it on some data."
            )
        if len(trainable_variables) != len(self.trainable_variables):
            raise ValueError(
                "Argument `trainable_variables` must be a list of tensors "
                "corresponding 1:1 to "
                f"{self.__class__.__name__}().trainable_variables. "
                f"Received list with length {len(trainable_variables)}, "
                f"but expected {len(self.trainable_variables)} variables."
            )
        if len(non_trainable_variables) != len(self.non_trainable_variables):
            raise ValueError(
                "Argument `non_trainable_variables` must be a list of tensors "
                "corresponding 1:1 to "
                f"{self.__class__.__name__}().non_trainable_variables. "
                f"Received list with length {len(non_trainable_variables)}, "
                f"but expected {len(self.non_trainable_variables)} variables."
            )

        # Gather variable mapping
        trainable_mapping = zip(self.trainable_variables, trainable_variables)
        non_trainable_mapping = zip(
            self.non_trainable_variables, non_trainable_variables
        )
        mapping = list(trainable_mapping) + list(non_trainable_mapping)

        # Caches info about `call()` signature, args, kwargs.
        call_spec = CallSpec(
            self._call_signature, self._call_context_args, args, kwargs
        )

View on GitHub (pinned to 7a34a03db6)

Solutions

  1. Pass exactly layer.non_trainable_variables to stateless_call
  2. Re-gather variables right before the call if the layer was rebuilt or quantized in between

Example fix

# before
out, _ = layer.stateless_call(x, layer.trainable_variables, [])  # layer has BN stats
# after
out, _ = layer.stateless_call(x, layer.trainable_variables, layer.non_trainable_variables)
Defensive patterns

Strategy: validation

Validate before calling

assert len(non_trainable_variables) == len(layer.non_trainable_variables)

Type guard

def nontrainable_match(layer, ntv):
    return len(ntv) == len(layer.non_trainable_variables)

Prevention

When it happens

Trigger: Passing an empty list for a layer with BN statistics; passing model-level non-trainable variables to a sublayer; forgetting RNG-state variables.

Common situations: Layers with metrics/BN state in functional loops; models quantized after variables were collected; variable set changed between gather and call.

Related errors


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