Skip to content

“Unknown (Magic, Version) Combination” Loading an Older GGML File

9 min read · updated August 11, 2026

error loading model: unknown (magic, version) combination: 67676a74, 00000002; is this really a GGML file? The file is real. It is a model format that predates GGUF, and current builds no longer contain the code to read it.

The message and the two numbers

Every ggml-family model file starts with a four-byte magic number identifying the format, followed by a version. The loader reads both, looks the pair up in a table of formats it understands, and refuses if the pair is not there. The two hex values in the message are exactly those bytes, and they are the most informative thing on the screen.

The message is followed by a load failure and, in wrappers, by something less specific — llama_init_from_file: failed to load model, or a Python exception about being unable to load the model. The magic line is typically several lines above whatever your framework printed, which is why people paste the wrong half into search.

Decoding the magic

The magic values are ASCII read as a little-endian integer, so they can be decoded by hand:

67676a74  ->  bytes 74 6a 67 67  ->  "tjgg"  reversed: "ggjt"
67676d6c  ->  "lmgg"  reversed: "ggml"
67676d66  ->  "fmgg"  reversed: "ggmf"
46554747  ->  "GGUF"  (already in order)

So 67676a74 is ggjt, one of the pre-GGUF generations, and the accompanying version number distinguishes revisions within it. The progression was ggml, then ggmf, then ggjt — each a breaking change to how tensors were laid out or how the vocabulary was stored — and then GGUF in August 2023, which replaced ad-hoc header fields with a general key-value metadata block precisely so that the next change would not require a new magic.

A different and more confusing case: seeing 46554747 in this error, which is GGUF. That means the file is GGUF and the binary is old enough to predate GGUF support, the exact inverse of the usual problem. Update the runtime and it loads. If you are unsure which side you are on, the magic tells you: GGUF means your build is too old, anything else means your file is.

Why support was removed rather than kept

Keeping a legacy loader sounds cheap and is not. Each old format encoded the tokeniser, the quantisation layout and the hyperparameters differently, so support means maintaining several parallel readers, all of which must keep working as the tensor types, the tokeniser handling and the KV-cache code evolve underneath them. The formats were also short-lived and inconsistently produced, so much of that code existed to handle files that only a handful of repositories ever shipped.

The point of GGUF is that this stops happening. Metadata is self-describing key-value pairs, so a new architecture adds keys rather than a new magic, and a reader that does not recognise a key can say so precisely instead of failing at byte four. That is also why the quantisation type is a key rather than part of the format identity: picking a quantisation is now a choice within one container instead of a choice of file format.

A practical consequence people discover late: the conversion scripts that turned old GGML files into GGUF were removed from the repository along with the loaders. They exist in the history and in old release tarballs, and they depended on Python packages of that era. Treating conversion as the obvious path is optimistic; it is a bisect-and-build exercise, not a one-liner.

Your three options, in order

  1. Download a GGUF of the same model. This is the right answer in almost every case. The models distributed as GGML in 2023 have GGUF equivalents, usually with better quantisation methods than existed then — the K-quants and the importance-matrix quants came after most of those files were made, so the replacement is smaller and better rather than merely newer. Check the licence on the repository you take it from; some weights are gated and the access terms come with the download.
  2. Pin an old binary, only to extract something. If the file is genuinely irreplaceable — a fine-tune whose original weights are gone — build llama.cpp at a commit from before GGUF landed, or use a release binary from that period. Do this in a container, keep it off anything exposed, and treat it as a one-time archaeology tool rather than a runtime. It has had no security fixes in years.
  3. Convert from the original weights, not from the GGML file. If you still have the safetensors or PyTorch checkpoint the GGML file was made from, ignore the GGML file entirely and run the current convert_hf_to_gguf.py. This is the only path that gives you a file made by maintained code, and it is the one to prefer whenever the source weights exist.

What does not work, despite being the first suggestion in many old threads: renaming the file, changing its extension, or passing a flag. The magic is inside the file, and no current build has a code path behind it.

One caveat on the second option. Wrappers pin their own llama.cpp, so “downgrade until it loads” means downgrading the wrapper too, and the version of llama-cpp-python that still reads ggjt wants a Python and a build toolchain of the same vintage. That is why the effort usually exceeds re-downloading a model that is freely available in GGUF, and why the third option — converting from the original weights — is the one worth the time when it is possible at all.

Not landing here again

The generalisable habit is to check the first four bytes before spending an hour on a model that will not load. It costs one command:

head -c 4 model.bin | xxd
# 00000000: 4747 5546   GGUF   -> current format
# 00000000: 6767 6a74   ggjt   -> pre-GGUF, will be rejected

# Windows PowerShell
Format-Hex -Path model.bin -Count 4

Two related errors are worth separating from this one. A file whose magic is unrecognised because it is not a model at all — an HTML error page saved under a .gguf name, which is what an unauthenticated download of a gated repository produces — gives you a different and more confusing set of bytes; that is the invalid-file-magic case. And a file that loads but produces nonsense has valid magic and a different problem entirely. Only a rejected pair of numbers means the format itself is the issue.