ControlNetModel.from_single_file problem

Hello I am trying to convert alfredplpl/ControlNetForSD2 ¡ Hugging Face to PyTorch

Using this script:


from diffusers import ControlNetModel

controlnet = ControlNetModel.from_single_file(
    "./models/picasso11OpenPose/control_picasso11_openpose.ckpt"
)
   

results in

Traceback (most recent call last):
File “c:\Users\fdasfasdfasd\Documents.venv\lib\site-packages\huggingface_hub\utils_http.py”, line 409, in hf_raise_for_status
response.raise_for_status()
File “c:\Users\fdasfasdfasd\Documents.venv\lib\site-packages\requests\models.py”, line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/config.json

File reads succesfully on my computer, but there seems to be an ‘Unauthorized for url’ on Huggingface’s side

1 Like

Strictly speaking, it’s not a hub bug, but a problem that occurred with the hub…?


This is not your local .ckpt “failing to read.” It is Diffusers trying to fetch extra configuration from the Hub while loading the checkpoint, and that fetch is failing because stabilityai/stable-diffusion-2-1 is now not publicly reachable (removed, made private, or gated). Your traceback shows Diffusers (via huggingface_hub) requesting:

https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/config.json

and receiving 401 Unauthorized.

That pattern is common with Diffusers “single file” loaders: a .ckpt/.safetensors contains weights, not a complete, self-describing “Diffusers config,” so Diffusers sometimes looks up a base model repo to reconstruct the architecture. When that repo cannot be accessed, you get a network error even though your weights are local. This exact failure mode is discussed in Diffusers issue threads where from_single_file() tries to download .../resolve/main/config.json from a base SD repo and gets 404/401. (GitHub)


Why Diffusers is contacting stabilityai/stable-diffusion-2-1 at all

1) A checkpoint file is “just parameters”

A .ckpt is typically a PyTorch pickle-based checkpoint holding tensors (and sometimes other objects). It generally does not contain everything needed to rebuild the model class (layer counts, channel multipliers, attention dims, etc.). Diffusers therefore needs a config from somewhere. (Hugging Face)

2) For SD2.x ControlNet, the config is meaningfully different

Your repo alfredplpl/ControlNetForSD2 includes a YAML config control_picasso11_openpose.yaml alongside the checkpoint. The YAML shows context_dim: 1024 and an OpenCLIP text embedder, which is characteristic of SD2-style conditioning. (Hugging Face)

So Diffusers’ loader heuristics decide “this looks like an SD2.x-derived ControlNet,” then it tries to anchor itself to the canonical SD2.1 base repo (in your case stabilityai/stable-diffusion-2-1) to find config. That remote anchor is what’s breaking.

3) The base repo being “gone” is now a real-world issue

Your linked HF forum thread (Nov 14, 2025) reports that stabilityai/stable-diffusion-2, stabilityai/stable-diffusion-2-1-base, and stabilityai/stable-diffusion-2-1 return 404 for users, with discussion that it may be retracted or made private. (Hugging Face Forums)
When a repo is private or gated, clients often see 401 (Unauthorized) unless properly authenticated, and sometimes platforms intentionally blur “not found vs not allowed.” HF staff have explained 401 commonly means “not authenticated” and the model is private or doesn’t exist. (Hugging Face Forums)


Root causes (what can produce this exact 401)

You have a few plausible causes. In your case it is likely a combination of (A) and (B):

A) The base repo is no longer publicly accessible

If stabilityai/stable-diffusion-2-1 was removed or made private, any attempt to fetch config.json from it will fail. Users reported exactly this disappearance in Nov 2025. (Hugging Face Forums)

B) You are not authenticated (or your script isn’t using your token)

Even if a repo exists, gated/private repos require an access token. A very common pitfall: you are logged into the website in your browser, but your Python environment is not authenticated, so the hub download gets 401. HF forum guidance explicitly ties 401 to missing auth when the model is private or missing. (Hugging Face Forums)
There are also cases where people believe they are logged in, but environment variables or cache locations make the CLI token invisible to the runtime. (GitHub)

C) from_single_file() is pulling config from the wrong place (known Diffusers behavior)

There are open/closed Diffusers issues where ControlNetModel.from_single_file() succeeds for some files but fails for others because it tries to fetch a base SD repo’s config.json. (GitHub)
This is not “your fault,” it is how the loader is designed when it cannot fully infer architecture from the checkpoint alone.


Solutions that work (ordered by practicality)

Solution 1 (best): Provide the original YAML config that ships with your checkpoint

Your ControlNet repo already includes control_picasso11_openpose.yaml. (Hugging Face)
If you pass that YAML to Diffusers, it does not need to fetch the base model config from stabilityai/stable-diffusion-2-1.

Diffusers supports an original_config_file argument for single-file loading in multiple contexts (maintainers explicitly recommend it for offline and single-file loads). (GitHub)

Example:

# deps:
#   pip install -U diffusers huggingface_hub omegaconf safetensors torch

from diffusers import ControlNetModel
import torch

controlnet = ControlNetModel.from_single_file(
    "./models/picasso11OpenPose/control_picasso11_openpose.ckpt",
    original_config_file="./models/picasso11OpenPose/control_picasso11_openpose.yaml",
    torch_dtype=torch.float16,
    local_files_only=True,  # prevents surprise hub fetches
)

controlnet.save_pretrained("./controlnet_picasso11_openpose_diffusers")

Why this works:

  • The YAML fully describes the original architecture (channels, blocks, context_dim, etc.). (Hugging Face)
  • local_files_only=True ensures Diffusers won’t quietly reach for Hub files. This aligns with Diffusers maintainer guidance for offline/single-file usage. (GitHub)

If you still see a hub call after adding original_config_file, it usually means the loader is trying to fetch some other component. For ControlNetModel alone, it generally should not need text encoders or tokenizers, so a remaining hub fetch is a hint something is misrouted (wrong API, wrong class, or a bug).


Solution 2 (most robust): Convert using the official conversion script

Diffusers themselves recommend converting .ckpt to Diffusers format using the script, and they emphasize original_config_file as a key argument. (Hugging Face)

Conceptually:

  • from_single_file() is convenient but can be fragile with custom checkpoints.
  • The conversion script is more deterministic and produces a standard Diffusers folder with config.json, weights, etc.

Typical flow (ControlNet conversion):

# clone diffusers
git clone https://github.com/huggingface/diffusers
cd diffusers

python scripts/convert_original_stable_diffusion_to_diffusers.py ^
  --checkpoint_path "C:\path\to\control_picasso11_openpose.ckpt" ^
  --original_config_file "C:\path\to\control_picasso11_openpose.yaml" ^
  --dump_path "C:\path\to\output\controlnet_diffusers" ^
  --controlnet

This is exactly the pattern the docs and forum recommend: pass --checkpoint_path, --original_config_file, and --dump_path, plus --controlnet. (Hugging Face)

After that:

from diffusers import ControlNetModel
controlnet = ControlNetModel.from_pretrained("C:/path/to/output/controlnet_diffusers")

Solution 3: Authenticate, if the base model is merely gated (not actually removed)

If stabilityai/stable-diffusion-2-1 still exists but is gated, you need:

  • access approved on the model page
  • a token
  • your Python environment actually using that token

A quick diagnostic is to try downloading just the config with huggingface_hub. If this returns 401, your runtime is not authorized:

from huggingface_hub import hf_hub_download

hf_hub_download("stabilityai/stable-diffusion-2-1", "config.json")

Background: HF staff have repeatedly explained 401 is what you see when you are not authenticated and the model is private or missing. (Hugging Face Forums)
And there are known cases where people “logged in” but their token is invalid/ignored due to environment/caching differences. (GitHub)

That said, your forum link suggests SD2.x repos may be gone from the Hub for many users, so authentication may not fix it. (Hugging Face Forums)


Solution 4: Use a different base SD2.x repo (local or community-hosted) for inference

Important distinction:

  • Converting/loading the ControlNet weights is one problem.
  • Actually running generation requires a base SD2.x pipeline (UNet, VAE, text encoder).

If the official SD2.1 repos are unavailable, you need an alternative base model source (local copy you already downloaded, or another host). The forum thread even lists unofficial mirrors, but you need to handle licensing and long-term availability yourself. (Hugging Face Forums)


“Same behavior on latest diffusers?”

Very likely yes, unless you change inputs (provide the YAML or convert).

The latest PyPI release as of Dec 8, 2025 is diffusers 0.36.0. (PyPI)
But the key problem is not “an old diffusers bug.” It is the loader’s dependency on remote config when it cannot fully resolve architecture locally. That behavior is documented and shows up in issues across versions. (Hugging Face)

So: upgrading alone rarely fixes “missing config.json from a base repo.” Supplying original_config_file or converting does.


Similar cases online (same failure pattern, different repo IDs)

These are worth reading because they match your situation closely:

  • Diffusers #9208: ControlNetModel.from_single_file() fails on some ControlNet weights because it tries to download a base SD repo config.json and gets 404. Same mechanism as your 401. (GitHub)
  • Diffusers #7935: explicit issue title about stabilityai/stable-diffusion-2-1 “does not appear to have a file named config.json.” (GitHub)
  • HF forum (Nov 2025): SD2.x repos returning 404, with discussion that the models may be retracted or private. (Hugging Face Forums)
  • Diffusers #7209: offline from_single_file() fails because additional config is needed; references original_config_file in code. (GitHub)
  • Diffusers discussion #6836: maintainer guidance for offline from_single_file(), including needing original_config_file. (GitHub)

Good references: papers, guides, and “how-to” material (relevant to your case)

Core papers (background)

  • ControlNet paper: explains why ControlNet is an adapter that reuses a base diffusion backbone. This is the conceptual reason you need the base model architecture right. (arXiv)
  • Latent Diffusion Models (LDM): the backbone concept that Stable Diffusion and many ControlNet checkpoints build on. (arXiv)

Practical guides and official docs (what to do)

  • Diffusers: “Load different Stable Diffusion formats”: recommends conversion scripts, emphasizes .safetensors security, and highlights original_config_file as critical. (Hugging Face)
  • HF forum: “How to convert ckpt to diffusers format”: canonical step-by-step and the exact key flags. (Hugging Face Forums)
  • Diffusers ControlNet docs: pipeline usage and model structure context. (Hugging Face)

Implementation detail (why config lookups happen)

  • Diffusers code contains mappings to locate canonical YAML configs and detect checkpoint types (CONFIG_URLS, key heuristics). This is part of how “single-file” loading decides what it is looking at. (Hugging Face)

What I would do in your exact case

  1. Stop relying on stabilityai/stable-diffusion-2-1 entirely for conversion. Your ControlNet repo already includes the correct YAML config. (Hugging Face)

  2. Load with:

    • original_config_file=...control_picasso11_openpose.yaml
    • local_files_only=True
      That directly addresses the “hub fetch for config.json” path. (GitHub)
  3. Save the converted model via save_pretrained().

  4. Separately solve the base model availability problem for inference. If official SD2.1 repos are truly gone, you need a local base model or an alternative host, and you should expect any code that hardcodes stabilityai/stable-diffusion-2-1 to break until updated. (Hugging Face Forums)


Quick summary

  • Your 401 happens because from_single_file() is trying to download config.json from stabilityai/stable-diffusion-2-1, not because your local .ckpt is unreadable. (GitHub)
  • SD2.x repos appear to be unavailable on the Hub for many users as of Nov 2025. (Hugging Face Forums)
  • Fix it by supplying the local YAML config (original_config_file=...yaml) or converting with the official script. (Hugging Face)
  • Upgrading Diffusers alone usually does not help because the failure is triggered by a missing/inaccessible remote dependency. (PyPI)