unslothai/unsloth · error · ValueError
ControlNet is not yet supported on the native sd.cpp engine;
Error message
ControlNet is not yet supported on the native sd.cpp engine; run on a GPU (diffusers) for ControlNet conditioning.
What it means
ValueError: ControlNet conditioning is not implemented on the native sd.cpp engine. Note the preceding no-op: controlnet tuples whose strength component (index 3) is None/0/0.0 are silently dropped (matching diffusers semantics), so this fires only for genuinely active ControlNet.
Source
Thrown at studio/backend/core/inference/sd_cpp_backend.py:2074
or mask_image is not None
or reference_images
or (upscale is not None and upscale > 1)
):
raise ValueError(
"img2img / inpaint / reference / upscale are not yet supported on the native "
"sd.cpp engine; run on a GPU (diffusers) for image-conditioned workflows."
)
if prompts is not None or seeds is not None:
raise ValueError(
"Batched prompt/seed lists are not supported on the native sd.cpp engine "
"(it renders serially); run on a GPU (diffusers) for batched generation, "
"or use batch_size for a serial native batch."
)
# strength 0/None disables ControlNet (matches diffusers), so no-op it rather than 400.
if controlnet is not None and controlnet[3] in (None, 0, 0.0):
controlnet = None
if controlnet is not None:
raise ValueError(
"ControlNet is not yet supported on the native sd.cpp engine; run on a GPU "
"(diffusers) for ControlNet conditioning."
)
cancel = threading.Event()
with self._generate_lock:
with self._lock:
state = self._state
if state is None:
raise RuntimeError(DIFFUSION_NOT_LOADED_MSG)
# A resident server can exit while idle; drop stale state and report not-loaded so the client gets the reload path, not a 500.
if (
state.mode == "server"
and state.server is not None
and not state.server.is_alive()
):
self._state = None
raise RuntimeError(DIFFUSION_NOT_LOADED_MSG)View on GitHub (pinned to 203007d190)
Solutions
- Run ControlNet workflows on the diffusers engine (GPU).
- Set the controlnet strength to 0/None (or omit controlnet) for native generation — it is treated as disabled.
Example fix
# before backend.generate(prompt='a cat', controlnet=(model, image, None, 0.8)) # after backend.generate(prompt='a cat', controlnet=(model, image, None, 0.0)) # no-op on native; or use diffusers
Defensive patterns
Strategy: validation
Validate before calling
if controlnet is not None and controlnet[3] not in (None, 0, 0.0):
route_to_diffusers_engine(request) # active ControlNet needs diffusers Type guard
def controlnet_is_active(controlnet) -> bool:
return controlnet is not None and controlnet[3] not in (None, 0, 0.0) Prevention
- Default controlnet to None in client presets instead of carrying diffusers defaults over.
- Strip strength-0 ControlNet rows client-side (the engine treats them as disabled anyway).
When it happens
Trigger: generate() with controlnet not None and strength not in (None, 0, 0.0) on the native engine.
Common situations: Reusing diffusers-era request payloads that always attach a ControlNet; UI defaulting a control model on.
Related errors
- img2img / inpaint / reference / upscale are not yet supporte
- Batched prompt/seed lists are not supported on the native sd
- LoRA is not supported for {state.family.name} on the native
- ControlNet is not supported for the '{fam.name}' model famil
- {_cn_fs.reason}
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/cc2d1d81fb86a804.
Report an issue: GitHub.