{"record":{"id":"c82933bf04370b1d","repo":"invoke-ai/InvokeAI","slug":"must-provide-the-same-number-of-block-out-channel","errorCode":null,"errorMessage":"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}.","messagePattern":"Must provide the same number of `block_out_channels` as `down_block_types`\\. `block_out_channels`: (.+?)\\. `down_block_types`: (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/util/hotfixes.py","lineNumber":162,"sourceCode":"        conditioning_embedding_out_channels: Optional[Tuple[int]] = (16, 32, 96, 256),\n        global_pool_conditions: bool = False,\n        addition_embed_type_num_heads=64,\n    ):\n        super().__init__()\n\n        # If `num_attention_heads` is not defined (which is the case for most models)\n        # it will default to `attention_head_dim`. This looks weird upon first reading it and it is.\n        # The reason for this behavior is to correct for incorrectly named variables that were introduced\n        # when this library was created...\n        # The incorrect naming was only discovered much ...\n        # later in https://github.com/huggingface/diffusers/issues/2011#issuecomment-1547958131\n        # Changing `attention_head_dim` to `num_attention_heads` for 40,000+ configurations is too backwards breaking\n        # which is why we correct for the naming here.\n        num_attention_heads = num_attention_heads or attention_head_dim\n\n        # Check inputs\n        if len(block_out_channels) != len(down_block_types):\n            raise ValueError(\n                f\"Must provide the same number of `block_out_channels` as `down_block_types`. \\\n                    `block_out_channels`: {block_out_channels}. `down_block_types`: {down_block_types}.\"\n            )\n\n        if not isinstance(only_cross_attention, bool) and len(only_cross_attention) != len(down_block_types):\n            raise ValueError(\n                f\"Must provide the same number of `only_cross_attention` as `down_block_types`. \\\n                    `only_cross_attention`: {only_cross_attention}. `down_block_types`: {down_block_types}.\"\n            )\n\n        if not isinstance(num_attention_heads, int) and len(num_attention_heads) != len(down_block_types):\n            raise ValueError(\n                f\"Must provide the same number of `num_attention_heads` as `down_block_types`. \\\n                    `num_attention_heads`: {num_attention_heads}. `down_block_types`: {down_block_types}.\"\n            )\n\n        if isinstance(transformer_layers_per_block, int):\n            transformer_layers_per_block = [transformer_layers_per_block] * len(down_block_types)","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/util/hotfixes.py#L144-L180","documentation":"The hotfixed ControlNetModel.__init__ in invokeai/backend/util/hotfixes.py requires block_out_channels and down_block_types to be equal-length tuples, mirroring diffusers' UNet/ControlNet config validation. Each down_block_type (e.g. 'CrossAttnDownBlock2D', 'DownBlock2D') needs a matching channel count. Mismatched lengths mean the block stack cannot be constructed, so a ValueError is raised at model instantiation.","triggerScenarios":"Instantiating ControlNetModel (or ControlNetModel.from_config) with block_out_channels=(320,640,1280) but down_block_types of length 4 (or vice versa), typically from a hand-edited config.json or a programmatically built config dict.","commonSituations":"Hand-writing a ControlNet config for a custom architecture; copying config fields from one model into another with a different depth; omitting entries when extending down_block_types; a corrupted/partially downloaded config.json for a model.","solutions":["Make len(block_out_channels) equal len(down_block_types) — add or remove entries so every down block has a channel count.","Load the original model's config.json and use its exact block_out_channels/down_block_types rather than editing by hand.","Build the config programmatically, e.g. derive block_out_channels from down_block_types length.","Re-download the model config if the file was truncated or corrupted."],"exampleFix":"// before\nControlNetModel(\n    down_block_types=(\"CrossAttnDownBlock2D\", \"CrossAttnDownBlock2D\", \"DownBlock2D\", \"DownBlock2D\"),\n    block_out_channels=(320, 640, 1280),\n)\n// after\nControlNetModel(\n    down_block_types=(\"CrossAttnDownBlock2D\", \"CrossAttnDownBlock2D\", \"DownBlock2D\", \"DownBlock2D\"),\n    block_out_channels=(320, 640, 1280, 1280),\n)","handlingStrategy":"validation","validationCode":"def validate_controlnet_config(config: dict) -> None:\n    d = len(config['down_block_types'])\n    o = len(config['block_out_channels'])\n    if d != o:\n        raise ValueError(\n            f'block_out_channels ({o}) must match down_block_types ({d})'\n        )\n\nvalidate_controlnet_config(config_dict)\nControlNetModel.from_config(config_dict)","typeGuard":"def has_matching_block_lengths(config: dict) -> bool:\n    n = len(config.get('down_block_types', []))\n    return (\n        isinstance(config.get('block_out_channels'), (list, tuple))\n        and len(config['block_out_channels']) == n\n    )","tryCatchPattern":"try:\n    model = ControlNetModel.from_config(config_dict)\nexcept ValueError as e:\n    if 'block_out_channels' in str(e) and 'down_block_types' in str(e):\n        n = len(config_dict['down_block_types'])\n        config_dict['block_out_channels'] = (\n            list(config_dict['block_out_channels']) + [1280] * (n - len(config_dict['block_out_channels']))\n        )[:n]\n        model = ControlNetModel.from_config(config_dict)\n    else:\n        raise","preventionTips":["Never hand-edit block_out_channels without updating down_block_types (and vice versa).","Load configs from the model's original config.json instead of retyping them.","Write a config sanity check that runs before ControlNetModel instantiation in tests.","When extending a model depth-first, extend both lists together in a single helper function."],"tags":["config-validation","diffusers","controlnet","model-config"],"backgroundTag":"config-list-length-mismatch","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}