GraphiteEditor/Graphite · error · std::io::Error

InvalidData

InvalidData

Error message

Invalid Tiff format

What it means

rawkit's TIFF reader validates the two-byte byte-order marker at the start of the stream: 0x49 0x49 ("II", little-endian) or 0x4D 0x4D ("MM", big-endian). Anything else returns std::io::Error with ErrorKind::InvalidData and this message — the classic "not actually a TIFF" guard. A file shorter than two bytes instead fails read_exact with UnexpectedEof, which is a different error.

Source

Thrown at libraries/rawkit/src/tiff/file.rs:16

use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom};

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Endian {
	Little,
	Big,
}

pub struct TiffRead<R: Read + Seek> {
	reader: R,
	endian: Endian,
}

impl<R: Read + Seek> TiffRead<R> {
	pub fn new(mut reader: R) -> Result<Self> {
		let error = Error::new(ErrorKind::InvalidData, "Invalid Tiff format");

		let mut data = [0_u8; 2];
		reader.read_exact(&mut data)?;
		let endian = if data[0] == 0x49 && data[1] == 0x49 {
			Endian::Little
		} else if data[0] == 0x4d && data[1] == 0x4d {
			Endian::Big
		} else {
			return Err(error);
		};

		reader.read_exact(&mut data)?;
		let magic_number = match endian {
			Endian::Little => u16::from_le_bytes(data),
			Endian::Big => u16::from_be_bytes(data),
		};
		if magic_number != 42 {
			return Err(error);

View on GitHub (pinned to c507b35645)

Solutions

  1. Verify the first bytes: a TIFF starts with II 2A 00 or MM 00 2A
  2. Sniff the real format (e.g. the image crate's guess_format or file(1)) and route to the correct decoder before calling rawkit
  3. Re-export or redownload a genuine TIFF if the file is simply the wrong content

Example fix

// before
let tiff = TiffRead::new(Cursor::new(&bytes))?; // any non-TIFF input errors here

// after: verify the TIFF magic before parsing
fn is_tiff(buf: &[u8]) -> bool {
	buf.starts_with(b"II\x2A\x00") || buf.starts_with(b"MM\x00\x2A")
}
if !is_tiff(&bytes) {
	return Err(Error::new(ErrorKind::InvalidData, "Not a TIFF file"));
}
let tiff = TiffRead::new(Cursor::new(&bytes))?;
Defensive patterns

Strategy: validation

Validate before calling

// Read and sniff the header before choosing a decoder
let buf = Vec::new();
file.read_to_end(&mut buf)?;
if !(buf.starts_with(b"II\x2A\x00") || buf.starts_with(b"MM\x00\x2A")) {
	return Err(Error::new(ErrorKind::InvalidData, "unsupported image format"));
}
let tiff = TiffRead::new(Cursor::new(buf))?;

Type guard

fn is_tiff(buf: &[u8]) -> bool {
	buf.starts_with(b"II\x2A\x00") || buf.starts_with(b"MM\x00\x2A")
}

Try / catch

// Distinguish "wrong format" from "truncated file" when surfacing to users
match err.kind() {
	ErrorKind::InvalidData => show("Unsupported file format"),
	ErrorKind::UnexpectedEof => show("File is truncated"),
	_ => show("Could not read file"),
}

Prevention

When it happens

Trigger: TiffRead::new() is handed a JPEG/PNG/WebP or arbitrary binary data; a file renamed to .tif/.tiff with different content; a buffer sliced from the wrong offset so the magic bytes are not at position 0.

Common situations: Format dispatch based on file extension rather than magic bytes; user-supplied uploads trusted by filename; RAW-processing pipelines where the container type is assumed but the payload is something else.

Related errors


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/19a8ac82d04a8f2a. Report an issue: GitHub.