Atheris is a coverage-guided fuzzer for Python, built on libFuzzer. You write a function that takes bytes, call atheris.Setup and atheris.Fuzz, and wrap the imports you want instrumented. Any uncaught exception is reported as a failure. For native extensions, pair it with a sanitizer.
What Python fuzzing finds
Pure Python rarely corrupts memory, so the findings are different: uncaught exceptions on odd input, infinite loops and runaway memory in parsers, and logic bugs if you add assertions. Code that wraps C, such as image libraries or parsers with native speedups, can still hit real memory errors, and that is where sanitizers come in.
A minimal harness
Based on the Atheris README:
import sys
import atheris
with atheris.instrument_imports():
import mylib
def test_one_input(data: bytes):
mylib.parse(data)
atheris.Setup(sys.argv, test_one_input)
atheris.Fuzz()
Setup takes the command line arguments and your entry function, which receives bytes. Fuzz starts the run and never returns. Splitting the two lets the fuzzer consume its own flags first.
Instrument what you want measured
Coverage in Python comes from bytecode instrumentation. The README offers three ways: the instrument_imports context manager, an instrument_func decorator, and an experimental instrument_all for modules already imported. Imports inside the with block, and their dependencies, are instrumented. A module imported before the block is not, so import order matters.
If coverage never grows, the first thing to check is whether the code under test was actually instrumented.
Turning bytes into typed values
Real functions often need a string and an integer, not raw bytes. Atheris offers a FuzzedDataProvider that carves typed values out of the input. This keeps the harness short and the mutation effective.
Native extensions
For C extension modules the README says the extension must be instrumented for fuzzing to be effective, and Atheris can be paired with AddressSanitizer or UndefinedBehaviorSanitizer to catch more bugs. Building from source may be needed so the libFuzzer version matches your Clang. Expect this to be the fiddly part, so start with pure Python targets to get comfortable.
Version support
The README says the current source supports Python 3.11 to 3.14 on Linux and macOS, with older versions only on PyPI. Check your interpreter version before debugging an install problem.
Making a failure useful
When the fuzzer finds an input that raises, it saves it. Reproduce by running the harness with that file as the argument, fix the code, and keep the file as a test case in your suite. Decide up front which exceptions are expected. A parser that raises ValueError on bad input is behaving correctly, so catch that type in the harness and let everything else count as a bug.
Where it fits
Run short fuzz sessions in CI over the saved inputs and longer ones on a schedule. Atheris has OSS-Fuzz integration, and OSS-Fuzz lists python as a supported language.
Quick check
Why does my Atheris run show no coverage growth?
Most often the target modules were not instrumented, for example because they were imported before the instrumentation block. Move the import inside it.
Does an exception from bad input count as a bug?
Only if you do not expect it. Catch the exception types your API documents, and let anything else fail the run.
Sources
Last reviewed: 19 October 2026.