A `.pyc`'s first bytes are a version-specific *magic number*, so `bad magic number` means the file was compiled by a *different* Python than the one loading it — not that it's corrupt or protected. Read the first two bytes as a little-endian integer and map it to a version: I tested every release, e.g. a7 0d 0d 0a = 3495 = Python 3.11, 2b 0e 0d 0a = 3627 = Python 3.14 (full table below). To fix it, run the file with the matching interpreter or recompile from the .py source. If you only have the .pyc and not the source, recover it with our in-browser .pyc Decompiler — it detects the version for you.
You try to import or run a compiled .pyc — one you extracted from an app, copied off another machine, or found in a __pycache__ folder — and Python refuses it with ImportError: bad magic number. It looks scary, but the cause is almost always mundane: the .pyc was built by a different Python version than the one you're running. Here's exactly what the error means, a tested magic-number table for Python 3.8–3.14 so you can identify the version a .pyc needs, and how to fix every common cause.
The error, and what it actually means
Every .pyc starts with a 4-byte magic number that identifies the exact CPython version that compiled it. When the interpreter loads a .pyc, it checks those bytes first; if they don't match its own, it stops immediately rather than run bytecode it might not understand. Loading a Python 3.11 .pyc under Python 3.14, for example, gives:
ImportError: bad magic number in 'h': b'\xa7\r\r\n'
Those four bytes — \xa7\r\r\n, i.e. a7 0d 0d 0a — are the give-away: the leading a7 is the 3.11 magic, and 0d 0a is a constant \r\n trailer every version shares. The file is fine; it just needs a 3.11 interpreter. bad magic number never means the .pyc is corrupt or somehow locked — only that the *version* doesn't match.
The magic number bumps on almost every CPython feature release (and sometimes mid-development), because each version can add or change bytecode opcodes. That's by design — it stops an interpreter from silently mis-executing another version's bytecode.
Python `.pyc` magic numbers, 3.8–3.14 (tested)
I read importlib.util.MAGIC_NUMBER from each real interpreter (via the official python:<ver>-slim images) so this table is measured, not copied. The magic is a little-endian 2-byte integer followed by the constant 0d 0a, which together make the 4 header bytes:
| Python | Magic number | First 4 bytes (hex) |
|---|---|---|
| 3.8 | 3413 | 55 0d 0d 0a |
| 3.9 | 3425 | 61 0d 0d 0a |
| 3.10 | 3439 | 6f 0d 0d 0a |
| 3.11 | 3495 | a7 0d 0d 0a |
| 3.12 | 3531 | cb 0d 0d 0a |
| 3.13 | 3571 | f3 0d 0d 0a |
| 3.14 | 3627 | 2b 0e 0d 0a |
Patch releases within a feature version share the same magic — 3.11.0 and 3.11.9 are both 3495 — so any 3.11.x interpreter runs a 3.11 .pyc. You only need to match the major.minor version, not the exact patch.
Identify which Python a `.pyc` needs
You don't need any tools to read the version — the first two bytes are enough. Open the file in binary mode, take the little-endian integer, and look it up:
import sysMAGIC = {3413: "3.8", 3425: "3.9", 3439: "3.10", 3495: "3.11",3531: "3.12", 3571: "3.13", 3627: "3.14"}with open(sys.argv[1], "rb") as f:head = f.read(4)n = int.from_bytes(head[:2], "little")print("first 4 bytes :", head.hex(" "))print("magic int :", n)print("compiled by : Python", MAGIC.get(n, "unknown/other"))
first 4 bytes : a7 0d 0d 0amagic int : 3495compiled by : Python 3.11
Prefer not to touch the shell? Drop the .pyc into our free .pyc Decompiler — it reads the magic number for you and shows a Python 3.11 · clean recovery badge (and does the same for 3.8 through 3.14), then decompiles the file in the same step.
The usual causes — and the fix for each
- Version mismatch (by far the most common). The
.pycwas built on a different Python than you're running. Identify the version from the table above, then run the file with a matching interpreter —pyenv install 3.11/uv python install 3.11, or apython:3.11Docker image. If you have the original.py, the simplest fix is to just delete the stale.pyc(or the whole__pycache__/) and let Python recompile it for your version. - A moved or rebuilt environment. Copying
__pycache__between machines, or changing a Docker base image (e.g.python:3.11→python:3.12), leaves.pycfiles from the old version. Clear__pycache__and reinstall/rebuild so everything is recompiled locally. - A `.pyc` extracted from a PyInstaller app.
pyinstxtractorgives you.pycfiles that need the *same* Python the app was frozen with, and older PyInstaller versions strip the header so tools can't even read the magic. If that's your case, see decompile a PyInstaller EXE for rebuilding a valid header. - A truncated or corrupted file. If the header is a version you *do* have but you still get an error, the body may be damaged. A short or garbled code object surfaces as a marshal error rather than a magic error:
EOFError: marshal data too short
Variants like bad marshal data (unknown type code) or bad marshal data (invalid reference) mean the same thing: the header matched, but the serialized code object after it is incomplete or mangled. Re-extract or re-download the file; there's nothing to "fix" in a byte-damaged .pyc.
You have the `.pyc` but not the source
If you can't get the matching interpreter and you don't have the original .py, the practical move is to recover the source from the .pyc itself. Our in-browser .pyc Decompiler runs pycdc (Decompyle++) via WebAssembly and returns readable Python for 3.0 through 3.12 (partial for 3.13) — with no install and the version detected for you. For 3.13/3.14 bytecode that full decompilers don't rebuild yet, the standard library's marshal + dis still read the strings and logic on any version — the tested method is in can you decompile a Python 3.14 .pyc.
A recovered .pyc gives back variable names, string literals and full logic — which is exactly why a .pyc is a cache format, not a protection layer. If you're shipping code, obfuscate the source before compiling so the recovered result is unreadable.
Identify and decompile a .pyc — free, in your browser
Drop in a .pyc and our decompiler reads its Python version and returns readable source (pycdc via WebAssembly, 3.0–3.12).
Open the .pyc DecompilerFree tools mentioned here
Related guides
Frequently asked questions
What does 'bad magic number' mean in a .pyc file?
It means the .pyc was compiled by a different Python version than the one trying to load it. The first four bytes of every .pyc are a version-specific magic number; if they don't match the running interpreter, Python refuses the file with 'bad magic number' rather than risk running incompatible bytecode. It does not mean the file is corrupt or protected — just that the version differs.
How do I find out which Python version compiled a .pyc?
Read the first two bytes of the file as a little-endian integer and map it to a version: 3.8=3413, 3.9=3425, 3.10=3439, 3.11=3495, 3.12=3531, 3.13=3571, 3.14=3627. In Python: int.from_bytes(open('x.pyc','rb').read(2), 'little'). Or drop the file into our .pyc Decompiler, which reads the magic number and shows the detected version automatically.
How do I fix a bad magic number error?
Run the .pyc with a matching Python version (identify it from the magic number, then install that version with pyenv/uv or use its Docker image). If you have the original source, the simplest fix is to delete the stale .pyc (or __pycache__) and let Python recompile it. If you only have the .pyc, decompile it back to source.
Why do I get 'bad marshal data' instead of 'bad magic number'?
'bad marshal data' (or 'marshal data too short') means the version header matched, but the serialized code object after the 16-byte header is truncated or corrupted. It's a damaged file, not a version mismatch — re-extract or re-download it. 'bad magic number' is about the version; 'bad marshal data' is about the body.
Does the magic number change with every Python release?
It changes on almost every feature release (3.11→3.12→3.13→3.14 are 3495→3531→3571→3627) because each can add or alter bytecode opcodes. Patch releases within a version share the same magic, so any 3.11.x runs a 3.11 .pyc — you only need to match major.minor.