unslothai/unsloth · info · RuntimeError

Diffusion generation was cancelled.

Error message

Diffusion generation was cancelled.

What it means

A benign cancellation sentinel: while building the ControlNet pipeline, the per-generation `cancel` Event was already set before the ControlNet model download/load began, so `_controlnet_pipe` raises RuntimeError(DIFFUSION_CANCELLED_MSG) instead of doing wasted work. The cancel Event is set under `_lock` by unload() or a superseding load, so this indicates a model swap raced an in-flight generation request.

Source

Thrown at studio/backend/core/inference/diffusion.py:4830

    def _controlnet_pipe(self, state: _LoadState, resolved_cn: Any, cancel: threading.Event) -> Any:
        """Build (once, cached) the family's diffusers ControlNet pipeline around the requested
        ControlNet model. The ControlNet model is a small extra module loaded via from_pretrained
        and cached by id; the pipeline is assembled with ``Pipeline.from_pipe(base,
        controlnet=model)`` -- reusing the resident base modules at their loaded dtype (no reload,
        no recast; torch_dtype=None for the same reason as _workflow_pipe). Raises a clear
        ValueError when the family declares no ControlNet classes."""
        fam = state.family
        pipe_cls_name = getattr(fam, "controlnet_pipeline_class", None)
        model_cls_name = getattr(fam, "controlnet_model_class", None)
        if not pipe_cls_name or not model_cls_name:
            raise ValueError(f"ControlNet is not supported for the '{fam.name}' model family.")
        import diffusers

        cn_model = self._cn_models.get(resolved_cn.id)
        if cn_model is None:
            if cancel.is_set():
                raise RuntimeError(DIFFUSION_CANCELLED_MSG)
            # resolve_controlnet accepts a bare owner/name without the trust gate and from_pretrained would execute a malicious
            # pickle, so run the same Hub malware preflight. It fails OPEN, so a remote repo also forces safetensors below.
            remote_cn = not getattr(resolved_cn, "is_local", False)
            if remote_cn:
                from utils.security import evaluate_file_security
                _cn_fs = evaluate_file_security(resolved_cn.path, hf_token = state.hf_token or None)
                if _cn_fs.blocked:
                    raise ValueError(_cn_fs.reason)
            # Keep at most one ControlNet resident, else swapping ControlNets accumulates until OOM.
            if self._cn_models or self._cn_pipes:
                self._cn_models.clear()
                self._cn_pipes.clear()
                clear_gpu_cache()
            import torch

            # state.dtype is the display string ("bfloat16"), so pass the real dtype and avoid a float32 load.
            cn_dtype = getattr(torch, str(state.dtype).replace("torch.", ""), None)
            # Force safetensors for an untrusted remote repo: if the Hub scan failed open, an embedded pickle would still deserialize.

View on GitHub (pinned to 203007d190)

Solutions

  1. Catch RuntimeError with the cancellation message and treat it as a no-op (the job was intentionally aborted).
  2. Re-issue the generate call after the new model finishes loading if the ControlNet result is still wanted.
  3. In orchestrating code, serialize load/unload and generate so cancels are observed before dispatching work.
Defensive patterns

Strategy: try-catch

Type guard

def is_cancel(exc: RuntimeError) -> bool:
    return "cancelled" in str(exc).lower()

Try / catch

try:
    result = diffusion.generate(**params)
except RuntimeError as e:
    if str(e) == DIFFUSION_CANCELLED_MSG:  # or compare against the exported constant
        return {"status": "cancelled"}  # benign abort, not an error
    raise

Prevention

When it happens

Trigger: A generate() call with ControlNet whose resolved ControlNet model is not cached; before `from_pretrained` starts, `cancel.is_set()` is True because unload() or a new load signaled this generation. The first check in the `cn_model is None` branch fires.

Common situations: User cancels or switches models right as a ControlNet job starts; a superseding load invalidates in-flight jobs; rapid UI interactions triggering unload during queued generations. Expected under normal concurrent use, not a bug.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/0e84149848db0490. Report an issue: GitHub.