Weights-only load failed with an UnpicklingError when you resume an older checkpoint
A checkpoint that loaded without complaint for months starts failing after a framework upgrade. Nothing about the file changed. The loader now refuses by default to reconstruct any Python object that is not a plain tensor, and a training checkpoint carries several — the optimizer class among them — so the load stops on the first one it meets.
The checkpoint is fine; the loader got stricter. It refuses to rebuild Python objects like the optimizer class. Allowlist the exact class the error names, ideally through the scoped context manager, and expect one or two more behind it.
What this failure is
A resume-time failure in which a checkpoint written under a permissive deserialization default is read under a restrictive one, so the loader refuses to reconstruct the non-tensor objects the checkpoint legitimately contains.
Why it happens (the mechanism)
Deserialization of a pickle is code execution, so the safe default is to rebuild nothing but tensors. Training checkpoints have always stored more than tensors, because resuming needs the optimizer, the scheduler and the run configuration. Those two facts were compatible only while the default was permissive; once it flipped, every previously written checkpoint became a file the reader declines to fully trust.
What you'll observe
- The file is intact and readable, so every corruption check comes back clean
- The same file loaded successfully under the previous framework release
- The message offers a way to disable the restriction, which is the fastest fix and the wrong default
- It surfaces at resume, typically hours into a queue slot, on every rank at once
Common symptoms and what they mean
| Symptom | Why it happens |
|---|---|
| _pickle.UnpicklingError: Weights only load failed, raised from inside the checkpoint loader | Reconstructing a pickle can execute arbitrary code, because the format stores instructions for rebuilding objects rather than only their data. Restricting the loader to tensors and primitives by default closes that hole, and it is the right default for a file arriving from anywhere but your own cluster. |
| WeightsUnpickler error: Unsupported global, naming a class such as an optimizer that was not an allowed global by default | A training checkpoint is not only tensors. It holds the optimizer, its parameter groups, the scheduler, the sampler position and the run's arguments, all of which are Python objects that the restricted loader will not rebuild. The first one encountered stops the load, so the reported class is whichever came first and not necessarily the only one. |
| A suggestion to call add_safe_globals or use the safe_globals context manager with the named class | Nothing is wrong with the checkpoint. It was written under a permissive default and is being read under a strict one, so the change of behaviour lives entirely in the reader. This is why file-level integrity checks all pass and why the same bytes still load in the older release. |
| Every rank raising the same error at the same point, because they are all reading the same common state | Reconstructing a pickle can execute arbitrary code, because the format stores instructions for rebuilding objects rather than only their data. Restricting the loader to tensors and primitives by default closes that hole, and it is the right default for a file arriving from anywhere but your own cluster. |
Which systems are affected
- Resuming any training checkpoint written before the loader's default changed
- Distributed checkpoint formats that store a common state dictionary alongside the sharded tensors
- Optimizer state, which pickles the optimizer class itself rather than only its numbers
- Learning-rate schedulers, dataloader samplers, and argument namespaces saved beside the weights
How to confirm this is the problem
Apply the following checklist to a small reproduction: each box below is a positive signal that you are looking at this exact failure rather than a sibling in the same taxonomy.
- ✓Read the class named after Unsupported global. If it is an optimizer, a scheduler or an argument container, this is the strict-loader change and not damage to the file.
- ✓Load the same file with the restriction disabled once, in a scratch process. Success proves the bytes are fine and the reader's default is what moved.
- ✓Check whether the checkpoint predates your current framework release. A file written under the permissive default is the whole precondition.
Example training logs (fingerprint)
_pickle.UnpicklingError: Weights only load failed. This file can still be loaded, to do so you have two options
WeightsUnpickler error: Unsupported global: GLOBAL torch.optim.adamw.AdamW was not an allowed global by default
Please use `torch.serialization.add_safe_globals([torch.optim.adamw.AdamW])` to allowlist this global
File ".../megatron/training/checkpointing.py", in _load_base_checkpoint
state_dict = dist_checkpointing.load_common_state_dict(checkpoint_name)Timestamps and exact values vary across runs, but the pattern. An info-level start, an early WARN, an ERROR carrying the symptom. Is the actual fingerprint you should alert on. The Denpex platform flags this combination automatically.
Root cause, fix & prevention, signed in
Sign up free to see why this failure really happens, the exact remediation steps, and the production-grade prevention pattern. You also get 3 free full diagnoses for your own training logs.
Sign up free. Unlock the full analysisNo credit card · 3 free diagnoses · Instant access
Why the recommended fix works
Allowlisting states, class by class, that you know what is in your own file. The loader keeps refusing everything you did not name, so the protection still applies to any checkpoint arriving from elsewhere. Disabling the restriction wholesale also loads the file, but it discards the protection for every object in it rather than for the one you inspected.
Code examples
// Typical pattern:
import torch.optim as optim
optimizer = optim.AdamW(model.parameters(), lr=3e-4)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_training_steps)
for step in range(num_training_steps):
optimizer.zero_grad()
loss = model(batch)
loss.backward()
optimizer.step()
scheduler.step()Adapt the snippet to your framework. The same pattern holds for PyTorch Lightning, Hugging Face Trainer, DeepSpeed, Megatron-LM, and vLLM training wrappers. Where the wrapper exposes a config flag (for examplelr_scheduler_type in Trainer), prefer the flag over the imperative API to keep the schedule declarative and reproducible.
Best practices by model family
| Model / Stack | Recommendation | Notes |
|---|---|---|
| Your own cluster's checkpoint | Allowlist the named classes | Keeps the restriction for everything you did not name. |
| One-off load in a script | Scoped safe-globals context manager | The relaxation ends with the call. |
| Checkpoint from an external source | Allowlist only, never disable | This is exactly the case the default protects. |
| Fleet-wide upgrade | Re-save once in the strict format | Removes the need for an allowlist on future resumes. |
With the fix vs without the fix
| Dimension | With the fix | Without the fix |
|---|---|---|
| Is the checkpoint damaged | No, only read under a stricter default | Investigated as corruption |
| How many classes will it name | One at a time, in the order encountered | Assumed to be the only one |
| Where to relax the rule | At the single call site | As a process-wide setting |
Real engineering notes
“The message helpfully offers two routes and people take the first one, because it is a single argument and the job is already an hour into its allocation. That choice then propagates: it gets committed to the training script, and from there into every job on the cluster, including the ones loading checkpoints pulled from a model hub. Spend the extra minute on the allowlist. The class names are printed for you.”
Visual fingerprint
checkpoint
├── model tensors -> rebuilt under the strict default
├── optimizer state
│ └── class AdamW -> REFUSED: unsupported global
├── lr scheduler
└── run arguments
load stops at the FIRST refusal; the ones below are not yet reportedRoot cause, fix & prevention
Frequently asked questions
Twelve targeted questions that engineers and on-call staff most commonly ask about this failure.
Is my checkpoint corrupted?
Why not just disable the restriction?
I allowlisted the class and got another error.
Can I stop this happening on every resume?
Don't just read the fix, diagnose your run
The encyclopedia tells you what went wrong. Denpex tells you what went wrong in YOUR training run. With your logs, your config, and your stack.