struct.error: unpack requires a buffer of N bytes (tested)

struct.error: unpack requires a buffer of 8 bytes (or 4, or 16) means your buffer's length isn't exactly what the format needs. We measured the four ways that happens on Python 3.13.5, including one where your fields add up to 6 and struct wants 8.

The rule

struct.unpack(fmt, buf) needs len(buf) == struct.calcsize(fmt) exactly. Not at least: exactly. The message is the same whether you passed too few bytes or too many, so print len(buf) and struct.calcsize(fmt) first.

1. Native alignment adds padding bytes

import struct

data = b"\x01\x00\x02\x00\x00\x00"          # a uint16 and a uint32, 6 bytes
for fmt in ["HI", "<HI", "=HI"]:
    print(fmt, "calcsize =", struct.calcsize(fmt), end=" -> ")
    try:
        print(struct.unpack(fmt, data))
    except struct.error as e:
        print("struct.error:", e)

Output:

HI calcsize = 8 -> struct.error: unpack requires a buffer of 8 bytes
<HI calcsize = 6 -> (1, 2)
=HI calcsize = 6 -> (1, 2)

Measured on Python 3.13.5 (x86-64 Linux): without a prefix, "HI" is 8 bytes, not 6: native mode aligns the 4-byte I to a 4-byte boundary, inserting 2 pad bytes. <, >, = or ! turn alignment off. For file formats and network data, always use one of those (and pick the byte order the format specifies). More in native format padding breaks unpack.

2. The last read is short

import struct, io

f = io.BytesIO(b"\x01\x00\x00\x00" * 2 + b"\x01\x00")   # 2.5 records of 4 bytes
while chunk := f.read(4):
    try:
        print(struct.unpack("<I", chunk))
    except struct.error as e:
        print("struct.error:", e, "| got", len(chunk), "bytes")

Output:

(1,)
(1,)
struct.error: unpack requires a buffer of 4 bytes | got 2 bytes

Measured: a file that isn't a whole number of records fails on the last chunk. f.read(4) returns up to 4 bytes, so check len(chunk) and decide whether a partial record means a truncated file (error) or a trailing remainder (skip). Sockets are worse: recv(4) can return fewer bytes even mid-stream, so loop until you have them.

3. Too many bytes gives the same error

import struct

data = b"\x01\x00\x00\x00\x02\x00\x00\x00extra"
try:
    struct.unpack("<I", data)
except struct.error as e:
    print("unpack:", e)
print("unpack_from:", struct.unpack_from("<I", data))
print("iter_unpack:", list(struct.iter_unpack("<I", data[:8])))

Output:

unpack: unpack requires a buffer of 4 bytes
unpack_from: (1,)
iter_unpack: [(1,), (2,)]

Measured: 13 bytes for a 4-byte format raises unpack requires a buffer of 4 bytes. To read a header from a larger buffer use unpack_from() (it ignores the rest; see unpack_from silently ignores trailing bytes). For a run of records, iter_unpack(), which needs a multiple of the size.

4. The bytes went through text mode

import struct

with open("/tmp/st/rec.bin", "w") as f:       # text mode by mistake
    f.write("\x01\x00\x00\x80")
with open("/tmp/st/rec.bin", "rb") as f:
    raw = f.read()
print(len(raw), raw)
try:
    print(struct.unpack("<I", raw))
except struct.error as e:
    print("struct.error:", e)

Output:

5 b'\x01\x00\x00\xc2\x80'
struct.error: unpack requires a buffer of 4 bytes

Measured: 4 “bytes” written with text-mode open(..., "w") came back as 5: \x80 was UTF-8 encoded as c2 80. Write binary data with "wb" and bytes, never str.

Frequently asked questions

What does struct.error: unpack requires a buffer of N bytes mean?

The buffer length doesn't equal struct.calcsize(fmt). We measured the same message for both too few and too many bytes.

Why is struct.calcsize larger than my fields add up to?

Without a byte-order prefix, struct uses native alignment. We measured 'HI' as 8 bytes and '<HI' as 6. Use <, >, = or ! for file and network formats.

How do I unpack only the start of a larger buffer?

struct.unpack_from(fmt, buf, offset). We measured unpack failing on 13 bytes and unpack_from returning the first field.

Why did my binary file come back with extra bytes?

It was written in text mode, so characters above 0x7F were UTF-8 encoded. We measured 4 intended bytes reading back as 5.

Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on x86-64 Linux: native vs prefixed formats, a file with a partial last record, too-long buffers with unpack, unpack_from and iter_unpack, and binary data written in text mode. Every block was run verbatim and its output pasted from that run. PlainHeaders is our extension; everything else here is built-in Python.