SixLabors/ImageSharp · error · InvalidImageContentException
The image is not a valid QOI image.
Error message
The image is not a valid QOI image.
What it means
QoiDecoderCore.ThrowInvalidImageContentException throws InvalidImageContentException with 'The image is not a valid QOI image.' as a shared guard for any structural mismatch found while parsing a QOI stream. It is called from ProcessHeader (bad magic marker, channels, or color-space bytes) and from ProcessPixels (unexpected end of stream / missing end marker), so any decode-stage QOI structural failure funnels here.
Solutions
- Confirm the file really is QOI: check the first four bytes are 'qoif' and the last 8 bytes are the end marker (0x01 followed by seven zero bytes).
- Re-export the image with a spec-compliant QOI encoder; if truncated, re-transfer the file.
- Let Image.Identify detect the real format first, then load with the matching decoder.
- Catch InvalidImageContentException around the load and surface a 'not a valid QOI file' message.
Example fix
// before
using var image = Image.Load("mystery.qoi");
// after
if (!File.ReadAllBytes("mystery.qoi").AsSpan(0, 4).SequenceEqual("qoif"u8))
{
throw new InvalidDataException("File lacks the QOI 'qoif' magic.");
}
using var image = Image.Load("mystery.qoi"); Defensive patterns
Strategy: validation
Validate before calling
// Verify QOI structural markers before decode.
byte[] all = File.ReadAllBytes(path);
bool ok = all.Length >= 22
&& all.AsSpan(0, 4).SequenceEqual("qoif"u8)
&& all[^8] == 0x01 && all[^7..].ToArray().All(b => b == 0);
if (!ok) throw new InvalidDataException("File is not a structurally valid QOI image."); Try / catch
try { using var img = Image.Load(path); }
catch (InvalidImageContentException) { HandleUnknownFormat(path); } Prevention
- Use Image.Identify to detect the true format instead of trusting extensions.
- Check for the 'qoif' magic and footer marker on all ingested .qoi files.
- Guard against truncated transfers with size/checksum validation.
- For arbitrary uploads, probe format first, then dispatch to the right decoder.
When it happens
Trigger: Loading a file that starts with the wrong 4-byte magic ('qoif' expected); a channels byte that is not 3 or 4; pixel data that terminates before the 0x01 8-byte QOI end marker is found during ProcessPixels.
Common situations: Renaming a non-QOI file to .qoi; truncated uploads/failed downloads cutting off the end marker; encoders omitting the footer; probing unknown binaries with the QOI decoder.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- The image has an invalid size: width =
- The ANI sequence references a missing frame resource.
- The ANI file does not contain any decodable animation steps.
- The stream does not contain an ANI RIFF container.
- The ANI RIFF container size is invalid.
AI-assisted analysis of SixLabors/ImageSharp@59ce6af6fc (2026-09-13).
Data as JSON: /api/errors/13fe57a8c8168c13.
Report an issue: GitHub.
Appendix: source
Thrown at src/ImageSharp/Formats/Qoi/QoiDecoderCore.cs:132
int channels = stream.ReadByte();
if (channels is -1 or (not 3 and not 4))
{
ThrowInvalidImageContentException();
}
int colorSpace = stream.ReadByte();
if (colorSpace is -1 or (not 0 and not 1))
{
ThrowInvalidImageContentException();
}
this.header = new QoiHeader(width, height, (QoiChannels)channels, (QoiColorSpace)colorSpace);
}
[DoesNotReturn]
private static void ThrowInvalidImageContentException()
=> throw new InvalidImageContentException("The image is not a valid QOI image.");
private void ProcessPixels<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixels)
where TPixel : unmanaged, IPixel<TPixel>
{
using IMemoryOwner<Rgba32> previouslySeenPixelsBuffer = this.memoryAllocator.Allocate<Rgba32>(64, AllocationOptions.Clean);
Span<Rgba32> previouslySeenPixels = previouslySeenPixelsBuffer.GetSpan();
Rgba32 previousPixel = new(0, 0, 0, 255);
// We save the pixel to avoid losing the fully opaque black pixel
// See https://github.com/phoboslab/qoi/issues/258
int pixelArrayPosition = GetArrayPosition(previousPixel);
previouslySeenPixels[pixelArrayPosition] = previousPixel;
byte operationByte;
Rgba32 readPixel = default;
Span<byte> pixelBytes = MemoryMarshal.CreateSpan(ref Unsafe.As<Rgba32, byte>(ref readPixel), 4);
TPixel pixel = default;
for (int i = 0; i < this.header.Height; i++)View on GitHub (pinned to 59ce6af6fc)