{"record":{"id":"852ca428e9a334fc","repo":"invoke-ai/InvokeAI","slug":"unexpected-control-input-type-type-control-inpu","errorCode":null,"errorMessage":"Unexpected control_input type: ${type(control_input)}","messagePattern":"Unexpected control_input type: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/app/invocations/denoise_latents.py","lineNumber":471,"sourceCode":"    @staticmethod\n    def prep_control_data(\n        context: InvocationContext,\n        control_input: ControlField | list[ControlField] | None,\n        latents_shape: List[int],\n        device: torch.device,\n        exit_stack: ExitStack,\n        do_classifier_free_guidance: bool = True,\n    ) -> list[ControlNetData] | None:\n        # Normalize control_input to a list.\n        control_list: list[ControlField]\n        if isinstance(control_input, ControlField):\n            control_list = [control_input]\n        elif isinstance(control_input, list):\n            control_list = control_input\n        elif control_input is None:\n            control_list = []\n        else:\n            raise ValueError(f\"Unexpected control_input type: {type(control_input)}\")\n\n        if len(control_list) == 0:\n            return None\n\n        # Assuming fixed dimensional scaling of LATENT_SCALE_FACTOR.\n        _, _, latent_height, latent_width = latents_shape\n        control_height_resize = latent_height * LATENT_SCALE_FACTOR\n        control_width_resize = latent_width * LATENT_SCALE_FACTOR\n\n        controlnet_data: list[ControlNetData] = []\n        for control_info in control_list:\n            control_model = exit_stack.enter_context(context.models.load(control_info.control_model))\n            assert isinstance(control_model, ControlNetModel)\n\n            control_image_field = control_info.image\n            input_image = context.images.get_pil(control_image_field.image_name)\n            # self.image.image_type, self.image.image_name\n            # FIXME: still need to test with different widths, heights, devices, dtypes","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/invocations/denoise_latents.py#L453-L489","documentation":"prep_control_data accepts a single ControlNetPolysliderField, a list of them, or None. Any other type (str, dict, int, etc.) falls through to this raise because the function cannot interpret how to turn it into control data for the denoising loop.","triggerScenarios":"Calling prep_control_data (via _old_invoke or invoke) with `control_input` that is neither a ControlField, a list, nor None — commonly a dict or JSON-decoded object that was never converted to the field type.","commonSituations":"Scripted graph assembly passing raw dicts instead of ControlField instances; plugin code mis-wiring controlnet outputs into denoise inputs; schema changes between InvokeAI versions renaming the field type.","solutions":["Pass a ControlNetField/ControlField instance, a list of them, or None","If you have a dict, construct the field: ControlField(**dict) before calling","Inspect the upstream node output type and connect the correct controlnet invocation output"],"exampleFix":"// before\nprep_control_data(control_input={\"control_model\": \"canny\"}, ...)\n// after\ncontrol = ControlField(control_model=\"canny\", image=img, control_weight=1.0)\nprep_control_data(control_input=control, ...)  # or [control]","handlingStrategy":"type-guard","validationCode":"if control_input is not None and not isinstance(control_input, (ControlField, list)):\n    raise TypeError(f\"control_input must be ControlField, list, or None, got {type(control_input)}\")","typeGuard":"def is_valid_control_input(x: object) -> bool:\n    if x is None:\n        return True\n    if isinstance(x, list):\n        return all(isinstance(i, ControlField) for i in x)\n    return isinstance(x, ControlField)","tryCatchPattern":"try:\n    ctrl = prep_control_data(control_input=ci, ...)\nexcept ValueError as e:\n    if \"Unexpected control_input type\" in str(e):\n        ci = ControlField(**ci) if isinstance(ci, dict) else None\n        ctrl = prep_control_data(control_input=ci, ...)\n    else:\n        raise","preventionTips":["Always take control_input from a ControlNet invocation output, never raw dicts","Convert JSON dicts to field objects at graph-load time","Keep custom nodes' output types aligned with the denoise input schema"],"tags":["python","valueerror","typeerror","controlnet"],"backgroundTag":"unexpected-argument-type","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}