lllyasviel/ControlNet · error · ValueError
provide num_res_blocks either as an int (globally constant)
Error message
provide num_res_blocks either as an int (globally constant) or as a list/tuple (per-level) with the same length as channel_mult
What it means
In ControlNet's LatentDiffusion (cldm.py), num_res_blocks may be an int (same count at every resolution level) or a per-level list whose length must equal channel_mult. This ValueError enforces that 1:1 correspondence.
Source
Thrown at cldm/cldm.py:106
if num_heads_upsample == -1:
num_heads_upsample = num_heads
if num_heads == -1:
assert num_head_channels != -1, 'Either num_heads or num_head_channels has to be set'
if num_head_channels == -1:
assert num_heads != -1, 'Either num_heads or num_head_channels has to be set'
self.dims = dims
self.image_size = image_size
self.in_channels = in_channels
self.model_channels = model_channels
if isinstance(num_res_blocks, int):
self.num_res_blocks = len(channel_mult) * [num_res_blocks]
else:
if len(num_res_blocks) != len(channel_mult):
raise ValueError("provide num_res_blocks either as an int (globally constant) or "
"as a list/tuple (per-level) with the same length as channel_mult")
self.num_res_blocks = num_res_blocks
if disable_self_attentions is not None:
# should be a list of booleans, indicating whether to disable self-attention in TransformerBlocks or not
assert len(disable_self_attentions) == len(channel_mult)
if num_attention_blocks is not None:
assert len(num_attention_blocks) == len(self.num_res_blocks)
assert all(map(lambda i: self.num_res_blocks[i] >= num_attention_blocks[i], range(len(num_attention_blocks))))
print(f"Constructor of UNetModel received num_attention_blocks={num_attention_blocks}. "
f"This option has LESS priority than attention_resolutions {attention_resolutions}, "
f"i.e., in cases where num_attention_blocks[i] > 0 but 2**i not in attention_resolutions, "
f"attention will still not be set.")
self.attention_resolutions = attention_resolutions
self.dropout = dropout
self.channel_mult = channel_mult
self.conv_resample = conv_resample
self.use_checkpoint = use_checkpointView on GitHub (pinned to ed85cd1e25)
Solutions
- Match lengths: num_res_blocks=[2,2,2,2] for channel_mult of length 4
- Or pass a single int (e.g. num_res_blocks=2) for a constant count
- Recount levels after any channel_mult edit
Example fix
# before channel_mult=(1,2,4,4), num_res_blocks=[2,2,2] # after channel_mult=(1,2,4,4), num_res_blocks=[2,2,2,2]
Defensive patterns
Strategy: validation
Validate before calling
if not isinstance(num_res_blocks, int):
assert len(num_res_blocks) == len(channel_mult), 'num_res_blocks must match channel_mult length' Type guard
def num_res_blocks_ok(nrb, channel_mult) -> bool:
return isinstance(nrb, int) or (isinstance(nrb, (list, tuple)) and len(nrb) == len(channel_mult)) Prevention
- When editing channel_mult in YAML, recount num_res_blocks in the same commit
- Prefer int form unless per-level asymmetry is required
When it happens
Trigger: Instantiating the ControlUNetModel with num_res_blocks as a list shorter/longer than channel_mult, e.g. channel_mult=(1,2,4,4) with num_res_blocks=[2,2,2].
Common situations: Editing ControlNet configs (train-img2img, control sd v15 yaml) and changing channel_mult without updating num_res_blocks; copying configs between models with different depth.
Related errors
- resize_method {self.__resize_method} not implemented
- inverted_residual_setting should be non-empty or a 4-element
- inverted_residual_setting should be non-empty or a 4-element
- unknown loss type '{loss_type}'
- Parameterization {self.parameterization} not yet supported
AI-assisted analysis of lllyasviel/ControlNet@ed85cd1e25 (2026-08-27).
Data as JSON: /api/errors/703eb54d54d58822.
Report an issue: GitHub.