invoke-ai/InvokeAI · error · ValueError
Unexpected control_input type: ${type(control_input)}
Error message
Unexpected control_input type: ${type(control_input)} What it means
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.
Source
Thrown at invokeai/app/invocations/denoise_latents.py:471
@staticmethod
def prep_control_data(
context: InvocationContext,
control_input: ControlField | list[ControlField] | None,
latents_shape: List[int],
device: torch.device,
exit_stack: ExitStack,
do_classifier_free_guidance: bool = True,
) -> list[ControlNetData] | None:
# Normalize control_input to a list.
control_list: list[ControlField]
if isinstance(control_input, ControlField):
control_list = [control_input]
elif isinstance(control_input, list):
control_list = control_input
elif control_input is None:
control_list = []
else:
raise ValueError(f"Unexpected control_input type: {type(control_input)}")
if len(control_list) == 0:
return None
# Assuming fixed dimensional scaling of LATENT_SCALE_FACTOR.
_, _, latent_height, latent_width = latents_shape
control_height_resize = latent_height * LATENT_SCALE_FACTOR
control_width_resize = latent_width * LATENT_SCALE_FACTOR
controlnet_data: list[ControlNetData] = []
for control_info in control_list:
control_model = exit_stack.enter_context(context.models.load(control_info.control_model))
assert isinstance(control_model, ControlNetModel)
control_image_field = control_info.image
input_image = context.images.get_pil(control_image_field.image_name)
# self.image.image_type, self.image.image_name
# FIXME: still need to test with different widths, heights, devices, dtypesView on GitHub (pinned to 0b6a024f2f)
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
Example fix
// before
prep_control_data(control_input={"control_model": "canny"}, ...)
// after
control = ControlField(control_model="canny", image=img, control_weight=1.0)
prep_control_data(control_input=control, ...) # or [control] Defensive patterns
Strategy: type-guard
Validate before calling
if control_input is not None and not isinstance(control_input, (ControlField, list)):
raise TypeError(f"control_input must be ControlField, list, or None, got {type(control_input)}") Type guard
def is_valid_control_input(x: object) -> bool:
if x is None:
return True
if isinstance(x, list):
return all(isinstance(i, ControlField) for i in x)
return isinstance(x, ControlField) Try / catch
try:
ctrl = prep_control_data(control_input=ci, ...)
except ValueError as e:
if "Unexpected control_input type" in str(e):
ci = ControlField(**ci) if isinstance(ci, dict) else None
ctrl = prep_control_data(control_input=ci, ...)
else:
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot achieve the target of num_channels={num_channels}.
- Unsupported control_lllite type: {type(control_lllite)}
- The Anima ControlNet-LLLite model '{lllite_field.control_mod
- This Anima ControlNet-LLLite adapter is an inpainting adapte
- Unsupported Anima ControlNet-LLLite adapter: expected 3 or 4
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/852ca428e9a334fc.
Report an issue: GitHub.