Skip to content

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.

Quick answer

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.

Checkpointing#weights-only#unpickling#torch-load#add-safe-globals#checkpoint#optimizer-state

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

SymptomWhy it happens
_pickle.UnpicklingError: Weights only load failed, raised from inside the checkpoint loaderReconstructing 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 defaultA 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 classNothing 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 stateReconstructing 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)

training.log (synthetic 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 analysis

No 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 reference pattern
// 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 / StackRecommendationNotes
Your own cluster's checkpointAllowlist the named classesKeeps the restriction for everything you did not name.
One-off load in a scriptScoped safe-globals context managerThe relaxation ends with the call.
Checkpoint from an external sourceAllowlist only, never disableThis is exactly the case the default protects.
Fleet-wide upgradeRe-save once in the strict formatRemoves the need for an allowlist on future resumes.

With the fix vs without the fix

DimensionWith the fixWithout the fix
Is the checkpoint damagedNo, only read under a stricter defaultInvestigated as corruption
How many classes will it nameOne at a time, in the order encounteredAssumed to be the only one
Where to relax the ruleAt the single call siteAs 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

What a training checkpoint actually holds
  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 reported
Only the tensors are rebuilt without question. The load halts on the first Python object it will not reconstruct, so the class named in the error is the first of possibly several rather than the only one.

Root 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?
No. The bytes are unchanged; the loader's default changed. Integrity checks pass because there is nothing wrong with the file.
Why not just disable the restriction?
It works, and it removes the protection for every object in the file rather than the one class you actually inspected. Once it is in the training script it applies to externally sourced checkpoints too.
I allowlisted the class and got another error.
Expected. The load stops at the first refusal, so the next unsupported class is only revealed once the first is permitted.
Can I stop this happening on every resume?
Re-save the checkpoint once under the strict format. Later resumes then contain nothing that needs allowlisting.

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.