invoke-ai/InvokeAI · error · ValueError
Must provide the same number of `only_cross_attention` as `d
Error message
Must provide the same number of `only_cross_attention` as `down_block_types`. `only_cross_attention`: {only_cross_attention}. `down_block_types`: {down_block_types}. What it means
The same hotfixed ControlNetModel.__init__ validates only_cross_attention: unless it is a single bool applied to all blocks, it must be a tuple/list whose length equals down_block_types. If it is a non-bool sequence of the wrong length, per-block attention routing is undefined, so a ValueError is raised.
Source
Thrown at invokeai/backend/util/hotfixes.py:168
# If `num_attention_heads` is not defined (which is the case for most models)
# it will default to `attention_head_dim`. This looks weird upon first reading it and it is.
# The reason for this behavior is to correct for incorrectly named variables that were introduced
# when this library was created...
# The incorrect naming was only discovered much ...
# later in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131
# Changing `attention_head_dim` to `num_attention_heads` for 40,000+ configurations is too backwards breaking
# which is why we correct for the naming here.
num_attention_heads = num_attention_heads or attention_head_dim
# Check inputs
if len(block_out_channels) != len(down_block_types):
raise ValueError(
f"Must provide the same number of `block_out_channels` as `down_block_types`. \
`block_out_channels`: {block_out_channels}. `down_block_types`: {down_block_types}."
)
if not isinstance(only_cross_attention, bool) and len(only_cross_attention) != len(down_block_types):
raise ValueError(
f"Must provide the same number of `only_cross_attention` as `down_block_types`. \
`only_cross_attention`: {only_cross_attention}. `down_block_types`: {down_block_types}."
)
if not isinstance(num_attention_heads, int) and len(num_attention_heads) != len(down_block_types):
raise ValueError(
f"Must provide the same number of `num_attention_heads` as `down_block_types`. \
`num_attention_heads`: {num_attention_heads}. `down_block_types`: {down_block_types}."
)
if isinstance(transformer_layers_per_block, int):
transformer_layers_per_block = [transformer_layers_per_block] * len(down_block_types)
# input
conv_in_kernel = 3
conv_in_padding = (conv_in_kernel - 1) // 2
self.conv_in = nn.Conv2d(
in_channels, block_out_channels[0], kernel_size=conv_in_kernel, padding=conv_in_paddingView on GitHub (pinned to 0b6a024f2f)
Solutions
- Pass a single bool (e.g. only_cross_attention=False) if the same value applies to all blocks.
- Otherwise provide exactly one boolean per down block: len(only_cross_attention) == len(down_block_types).
- Derive it programmatically: only_cross_attention = [False] * len(down_block_types).
- Compare against the reference model's config.json and copy the field verbatim.
Example fix
// before
ControlNetModel(
down_block_types=("CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "DownBlock2D", "DownBlock2D"),
only_cross_attention=(True, False),
)
// after
ControlNetModel(
down_block_types=("CrossAttnDownBlock2D", "CrossAttnDownBlock2D", "DownBlock2D", "DownBlock2D"),
only_cross_attention=(True, False, False, False),
) Defensive patterns
Strategy: validation
Validate before calling
def validate_only_cross_attention(config: dict) -> None:
oca = config.get('only_cross_attention', False)
n = len(config['down_block_types'])
if not isinstance(oca, bool) and len(oca) != n:
raise ValueError(
f'only_cross_attention ({len(oca)}) must match down_block_types ({n})'
)
validate_only_cross_attention(config_dict) Type guard
def only_cross_attention_is_valid(config: dict) -> bool:
oca = config.get('only_cross_attention', False)
if isinstance(oca, bool):
return True
return isinstance(oca, (list, tuple)) and len(oca) == len(config.get('down_block_types', [])) Try / catch
try:
model = ControlNetModel.from_config(config_dict)
except ValueError as e:
if 'only_cross_attention' in str(e):
config_dict['only_cross_attention'] = False # uniform value applies to all blocks
model = ControlNetModel.from_config(config_dict)
else:
raise Prevention
- Prefer a single bool for only_cross_attention unless per-block control is truly needed.
- Derive per-block lists with [x] * len(down_block_types) rather than hand-writing them.
- Validate tuple lengths against down_block_types in a shared config-checking utility.
- Copy attention-related fields verbatim from the source model's config.json.
When it happens
Trigger: Passing only_cross_attention=(True, False) (length 2) to a 4-block ControlNetModel; passing a list produced by slicing or per-block logic that doesn't match the down_block_types length.
Common situations: Migrating configs between models of different block counts; generating only_cross_attention programmatically from a different-length list; typos in hand-edited config.json where a bool was expanded to a partial list.
Related errors
- Must provide the same number of `block_out_channels` as `dow
- Must provide the same number of `num_attention_heads` as `do
- {self.__class__} has the config param `addition_embed_type`
- `encoder_hid_dim` has to be defined when `encoder_hid_dim_ty
- encoder_hid_dim_type: {encoder_hid_dim_type} must be None, '
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/38852a7d24c9bdc4.
Report an issue: GitHub.