Hugging Face, End to EndLesson 6 of 75

Contents

Hugging Face, End to End

Lesson 6 of 75. Sign in to keep your place.

Module 1 · The Hub, read properly

Loading a model can run someone else’s code

9 min read

The thing nobody tells beginners

A PyTorch .bin or .pt checkpoint is a Python pickle. Pickle is not a data format in the way JSON is a data format. It is a small instruction stream for rebuilding Python objects, and among its instructions is one that calls an arbitrary function. Unpickling therefore executes code by design.

So torch.load("pytorch_model.bin") on a file from a stranger is, in the general case, running a program that stranger wrote, with your user’s permissions, on your machine. Not a theoretical risk: malicious models have been found on the Hub, and the usual payload is boring and effective: read environment variables, find the tokens, send them somewhere.

This is the reason safetensors exists, and it is why the format is worth understanding rather than merely preferring.

What safetensors actually is

A .safetensors file has two parts:

  1. A header: a JSON object listing every tensor, its dtype, its shape, and the byte offsets where its data begins and ends.
  2. The raw bytes of the tensors, one after another.

That is the whole specification. There is no instruction stream, no object graph, no code path that can be made to call a function. Loading it parses JSON and then points at memory.

Two consequences follow from that design, and both are practical rather than security-flavoured:

  • It is faster. Because tensors sit at known offsets, the loader can memory-map the file and hand the framework a view of it rather than copying. On large models this turns a minute into a few seconds, and it is why cold starts improved across the ecosystem when repositories converted.
  • You can read one tensor without reading the file. The header tells you offsets, so a tool can fetch a single layer over HTTP with a range request. That is how the Hub renders a tensor listing in the browser for a 70 GB model without downloading it.
Python
from safetensors.torch import load_file
weights = load_file("model.safetensors")   # no code executes

Prefer safetensors, and say so in code

Transformers picks safetensors automatically when both formats are present. When you want the guarantee rather than the default, ask for it:

Python
model = AutoModel.from_pretrained("some-org/some-model", use_safetensors=True)

If that raises, the repository has no safetensors copy, and you now have a decision to make rather than a silent risk. Many older repositories have one because a community contributor opened a conversion pull request. Check the Community tab before you conclude there is none.

The hole safetensors does not plug: trust_remote_code

Some models use architectures that are not in the transformers library. Their repositories ship their own Python (modeling_custom.py, configuration_custom.py), and loading them requires:

Python
model = AutoModel.from_pretrained("some-org/new-architecture", trust_remote_code=True)

That flag means exactly what it says. It downloads Python from the repository and imports it. Safetensors weights do not protect you here at all, because the danger is no longer in the weights file.

The flag is not avoidable (new architectures need it for months before library support lands) so treat it as a decision with steps:

  • Read the .py files in Files and versions first. They are usually a few hundred lines of ordinary modelling code. Anything doing network calls, subprocess work or environment reads is a stop.
  • Pin the revision, so a later commit cannot change the code under you: revision="<commit hash>" alongside trust_remote_code=True.
  • Prefer well-known organisations for this specifically, not because large labs are virtuous but because their repositories are watched by thousands of people.
  • Run it in a container or a throwaway Colab session when you can, with no tokens in the environment.

The Hub’s own scanning, and its limits

Uploads are scanned: a pickle scanner that inspects the opcode stream for dangerous imports, plus third-party malware and secret scanning, and the results appear on the file listing. This is real and it catches real things.

It is also a heuristic on an adversarial problem. A scanner that lists suspicious imports can be evaded by an attacker who reaches the same call through an indirection the scanner does not model. Treat a clean scan as evidence, not as a guarantee, in the same way you treat an antivirus pass on a downloaded installer.

The five-second habit

Before loading anything from an account you do not recognise:

  1. Are there .safetensors files? Use them.
  2. Does loading need trust_remote_code? If yes, read the Python.
  3. Is there a pinned revision in your code? If no, add one.
  4. Are there tokens in this environment? If yes, and you are unsure, move to a session without them.

Do this now

Find a repository that requires trust_remote_code=True and open its modeling_*.py in the browser. Read the imports at the top. That list (what the file is allowed to reach for) is the entire security review most of the time, and it takes about thirty seconds.

Every lesson here is open. Your progress is recorded once you pass the Image Generation, in Practice exam.

Check your understanding

A team standardises on use_safetensors=True for every model they load, and also loads a new vision-language architecture with trust_remote_code=True. What risk remains?

Pick the one you would defend. Your answer stays private.

Next lesson · 8 minFour shelves, and picking the wrong one costs a weekend

Stuck on something?Ask Arjun, your AI tutor

How to cite: Addaly (2026) ‘Loading a model can run someone else’s code’, in Hugging Face, End to End. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/hugging-face/safetensors-and-pickle (Accessed: 13 October 2026).