What's inside a .safetensors file
3 min read · Updated July 29, 2026
safetensors exists because the format it replaced could execute code. A PyTorch .bin checkpoint is a pickle, and unpickling runs arbitrary Python by design. safetensors is the minimum format that stores the same tensors without that property: three parts, no executable content, and no parser complex enough to hide a vulnerability.
The layout
- 8 bytes:
N, an unsigned little-endian 64-bit integer, the size of the header. - N bytes: a JSON object mapping tensor name to
{"dtype", "shape", "data_offsets"}. - The rest: tensor data, concatenated.
{
"model.embed.weight": {
"dtype": "F16",
"shape": [32000, 4096],
"data_offsets": [0, 262144000]
},
"model.layers.0.mlp.gate.weight": {
"dtype": "BF16",
"shape": [11008, 4096],
"data_offsets": [262144000, 352321536]
},
"__metadata__": { "format": "pt" }
}__metadata__ key holds free-form string pairs. Every other key is a tensor.A complete reader
The format is small enough to parse without a library, which is a useful property when debugging a checkpoint that will not load.
import json, struct, numpy as np
DTYPES = {"F64": "f8", "F32": "f4", "F16": "f2", "I64": "i8",
"I32": "i4", "I16": "i2", "I8": "i1", "U8": "u1", "BOOL": "?"}
with open("model.safetensors", "rb") as f:
n = struct.unpack("<Q", f.read(8))[0]
header = json.loads(f.read(n))
buf = f.read()
for name, meta in header.items():
if name == "__metadata__":
continue
start, end = meta["data_offsets"]
print(name, meta["dtype"], meta["shape"], end - start)
if meta["dtype"] in DTYPES: # BF16 has no NumPy dtype
a = np.frombuffer(buf[start:end], dtype=DTYPES[meta["dtype"]])
arr = a.reshape(meta["shape"])Why pickle was unsafe
Pickle is a stack language for reconstructing Python objects, and two of its opcodes make it a code execution primitive: GLOBAL resolves and imports an arbitrary module attribute, and REDUCE calls a callable with arguments from the stack. Together they let a payload import and invoke anything available in the loading environment.
The Python documentation states plainly: "The pickle module is not secure. Only unpickle data you trust." A checkpoint downloaded from a model hub is not data you trust.
PyTorch's weights_only=True restricts unpickling to a permitted set of types and is the default in recent versions. It substantially reduces the attack surface but works by constraining a general-purpose deserializer, rather than by using a format that cannot express code in the first place.
| pickle (.bin, .pt) | safetensors | |
|---|---|---|
| Can express code | Yes, by design | No: JSON plus a byte buffer |
| Read the tensor list cheaply | No: requires deserializing | Yes: header only |
| Lazy / partial load | Whole-file mmap only (torch.load(mmap=True)) | Per-tensor: offsets are known upfront |
| Zero-copy on CPU | No | Yes: buffer can be mapped |
| Arbitrary Python objects | Yes | No: tensors only |
What it deliberately does not do
- No compression. Tensor bytes are stored raw, so the file is the size of its tensors and the buffer can be mapped directly; compression would force a decode pass.
- No nesting. The header is a flat name-to-tensor map. Module structure lives in the dotted names, by convention only.
- No arbitrary metadata types.
__metadata__is string-to-string. Anything richer has to be encoded by the caller. - No optimizer state or Python objects. Checkpoints that need those still use a pickle-based format alongside.
Because the inventory is just the header, listing every tensor in a 30 GB checkpoint costs one small read. The safetensors viewer uses exactly that: it reads the header locally and lists names, dtypes and shapes without loading any weights.
Sources
- huggingface/safetensors (the format specification)
- Python: pickle module documentation (the security warning and opcode semantics)
- EleutherAI: safetensors security audit (third-party audit of the reference implementation)
- PyTorch: torch.load and weights_only
Related articles
Inspect these files
ViewKit opens these formats in the browser. The file is parsed locally and never uploaded.