unslothai/unsloth · error · RuntimeError

A diffusion load is already in progress.

Error message

A diffusion load is already in progress.

What it means

RuntimeError raised under the backend lock when begin_load is called while a previous load is still in flight (self._loading is set without an error). One diffusion load at a time is enforced; the second caller fails fast instead of interleaving downloads/state swaps.

Source

Thrown at studio/backend/core/inference/sd_cpp_backend.py:1233

        # native engine does not read the diffusers base: FLUX.2 takes its VAE from
        # unsloth/FLUX.2-VAE and its encoders from another repo again, so recording only the base
        # left every repo the pick really depends on outside the guard, and an unloaded model's
        # encoder could be deleted while its GGUF stayed installed. Best-effort bookkeeping;
        # never fails a load.
        try:
            from hub.utils.companion_assets import record_companion_link
            for asset_repo in dict.fromkeys(
                r
                for r, _f, kind in self._asset_specs(repo_id, gguf_filename, fam, inner_dim)
                if kind != "diffusion_model"
            ):
                record_companion_link(repo_id, asset_repo)
            record_companion_link(repo_id, base)
        except Exception as exc:  # noqa: BLE001
            logger.debug("sd_cpp.companion_link_record_failed: %s", exc)
        with self._lock:
            if self._loading is not None and self._loading.error is None:
                raise RuntimeError("A diffusion load is already in progress.")
            # A superseding load must stop any in-flight generation, else the old run can still persist an image after the new load starts.
            if self._active_generate_cancel is not None:
                self._active_generate_cancel.set()
            self._load_token += 1
            token = self._load_token
            # A NEW event per load, never a clear() of the shared one: unload() sets the event the running worker holds but also
            # drops _loading, so a clear() here would un-cancel its still-running multi-gigabyte pull.
            cancel_event = threading.Event()
            self._cancel_event = cancel_event
            self._loading = _SdLoading(
                repo_id = repo_id,
                base_repo = base,
                asset_repos = tuple(
                    dict.fromkeys(
                        r
                        for r, _f, kind in self._asset_specs(repo_id, gguf_filename, fam, inner_dim)
                        if kind != "diffusion_model"
                    )

View on GitHub (pinned to 203007d190)

Solutions

  1. Wait for the in-flight load to finish (poll status()) before calling begin_load again.
  2. Fix the caller so only one load is issued at a time (disable the button / serialize requests).
  3. If the stuck load never completes, call unload() to clear the in-flight state, then reload.

Example fix

# before
backend.begin_load(repo_a, gguf_a)  # still running
backend.begin_load(repo_b, gguf_b)  # RuntimeError

# after
while backend.status().get('loading'):
    time.sleep(0.5)
backend.begin_load(repo_b, gguf_b)
Defensive patterns

Strategy: validation

Validate before calling

status = backend.status()
if status.get('loading'):
    wait_for_load_complete(backend)  # poll status until not loading

Try / catch

try:
    backend.begin_load(repo_id=r, gguf_filename=f)
except RuntimeError as e:
    if 'already in progress' in str(e):
        wait_for_load_complete(backend)
        backend.begin_load(repo_id=r, gguf_filename=f)
    else:
        raise

Prevention

When it happens

Trigger: Two concurrent begin_load calls on the same backend instance (double-clicked load in UI, parallel MCP requests, retry fired before the first load finished).

Common situations: Frontends that fire load on every settings change; automation retrying a slow load without waiting; UI not disabling the load button during a load.

Related errors


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