A gated repository returns 403 for “you have not asked”, “you asked and are waiting”, and “you have access but this process is not you”. Three unrelated situations, one status code, and the message text is what separates them.
The string
The huggingface_hub library raises a typed exception, and its own documentation gives the message verbatim:
huggingface_hub.errors.GatedRepoError: 403 Client Error. (Request ID: ViT1Bf7O_026LGSQuVqfa)
Cannot access gated repo for url https://huggingface.co/api/models/ardent-figment/gated-model.
Access to model ardent-figment/gated-model is restricted and you are not in the authorized list.
Visit https://huggingface.co/ardent-figment/gated-model to ask for access.
Through Transformers the same condition usually surfaces as OSError: You are trying to access a gated repo with the hub message underneath. Through curl or a downloader that does not use the library, you get a bare 403 and a JSON body, and you lose the sentence that tells you which state you are in — which is a good reason to reproduce the failure through the library once before debugging it.
GatedRepoError derives from RepositoryNotFoundError for backward compatibility, so an except RepositoryNotFoundError block will swallow it and report a missing model. If your own tooling says “model not found” for a model you can see in a browser, check that first.
Three states, one status code
- Not requested. The message says you are not in the authorized list and points at the model page. Nothing is wrong with your token; you have not accepted the terms. Open the model page while signed in, read the licence, and submit the form.
- Requested, not yet granted. Some gates are automatic and some are reviewed by the publisher, and the reviewed ones can sit pending for a while. The wording from the API is the same. Your account settings list your gated repositories and their state, and that page is the authoritative answer to “has it been granted yet”.
- Granted to a different identity than the one calling. This is the most common one among people who are certain they have access, because they do — in the browser. The process is unauthenticated, or authenticated as another account, or holding a token that cannot read gated repositories.
A fourth possibility that looks identical from the outside: access is granted per repository, not per organisation or per model family. Being approved for one variant of a model does not approve you for a re-upload of the same weights under someone else’s namespace, and it does not approve the base model when you were approved for the instruct one. Check the exact repository id in the error against the exact one in your settings.
Checking which identity is being used
Two commands settle the third case in under a minute:
hf auth whoami
hf auth login
If whoami prints a different user than the one that accepted the licence, or reports that you are not logged in, that is the bug. Older installations use the huggingface-cli entry point with the same subcommands; both talk to the same token file.
Then confirm the specific repository resolves for that identity, rather than testing with a whole download:
python -c "from huggingface_hub import model_info; print(model_info('meta-llama/Llama-3.1-8B-Instruct').id)"
That call is cheap, hits the same authorisation path as the download, and raises the same typed error. If it succeeds and your download still fails, the download is running as a different user or in a different environment — a service account, a container, a notebook kernel started before you logged in.
Watch the precedence, because it is a common way to debug the wrong thing for an hour. The hub library prefers an explicit token passed in code, then the HF_TOKEN environment variable, then the stored login. So a stale HF_TOKEN exported in a shell profile months ago will quietly override a fresh hf auth login, and whoami will report whichever one won rather than the one you think you are using. Print the variable before you trust the login:
python -c "import os; print('HF_TOKEN set:', bool(os.environ.get('HF_TOKEN')))"
Token scope, which is the modern trap
Hugging Face tokens are fine-grained. A token can be valid, belong to the right account, and still be refused, because it was created without permission to read the gated repositories that account has been granted. The account-level grant and the token-level permission are two separate switches and both must be on.
When a token is the suspect, the fastest test is to swap in a token with broad read access temporarily. If that works, the original token’s permissions were the problem and you can grant the specific repository rather than leaving the broad token in place.
The names and granularity of token permissions are part of Hugging Face’s product and have changed more than once. Treat any checkbox name you read in a thread as a hint and confirm against the current settings UI. Hugging Face documents the current behaviour in its security tokens documentation.
On the licence itself: gated weights are gated because the publisher attached terms, and those terms usually govern redistribution and sometimes commercial use. Mirrors that route around the gate exist; using them does not transfer the licence, and it leaves you with weights you cannot account for. The gate is the supported access path. What the terms actually say varies by publisher — the Llama 3 licence and the Gemma terms are worked examples, and open weights is not open source covers why the distinction matters.
Servers, containers and CI
Headless environments fail this way constantly, because the token file that hf auth login writes lives in the home directory of the user that ran it, and the process doing the download frequently is not that user. The portable fix is the environment variable, which the hub library reads without any interactive step:
export HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxx
python download_weights.py
Three things that go wrong around this specifically. A Docker build does not inherit your host environment, so the token must be passed as a build secret rather than baked into a layer. A systemd unit starts with an almost empty environment. And a CI runner needs the token as a masked secret, not a literal — a token committed to a repository is a credential leak whether or not the repository is public.
Finally, pin what you download. A gated repository can change its files under the same name, and a build that resolves main is not reproducible; pinning to a commit hash fixes that, and deploying model weights covers getting them onto the machine that needs them.
Top comments (0)