SixLabors/ImageSharp · error · InvalidImageContentException
The ANI animation header declares an invalid size.
Error message
The ANI animation header declares an invalid size.
What it means
The ANI header contains a self-describing 'cbSize' (BytesInHeader) field, typically 36. The decoder validates that this declared size is at least the structural header size and no larger than the enclosing chunk's size; any other value makes the header internally inconsistent, so InvalidImageContentException is thrown. This guards against headers whose in-file size field contradicts the actual chunk.
Solutions
- Ensure the header's cbSize field equals the 'anih' chunk size (normally 36) — patch or regenerate the header.
- Re-export the ANI with a conformant tool so cbSize and chunk size agree.
- If you write ANI files yourself, set the JIF/cbSize field to sizeof(ANIHEADER) = 36 when serializing.
Example fix
// before: cbSize=0 in the anih payload // after: Marshal.SizeOf<AniHeader>() == 36 written into the header at offset 0 header.BytesInHeader = 36;
Defensive patterns
Strategy: validation
Validate before calling
// The first 4 bytes of anih payload are cbSize; require it to be 36
static bool AnihCbSizeValid(string path)
{
using var fs = File.OpenRead(path);
Span<byte> buf = stackalloc byte[8];
fs.Seek(12, SeekOrigin.Begin);
while (fs.Read(buf) == 8)
{
uint sz = BinaryPrimitives.ReadUInt32LittleEndian(buf.Slice(4, 4));
if (buf.Slice(0, 4).SequenceEqual("anih"u8))
{
Span<byte> pay = stackalloc byte[4];
return fs.Read(pay) == 4 && BinaryPrimitives.ReadUInt32LittleEndian(pay) == 36;
}
fs.Seek((long)((sz + 1) & ~1u), SeekOrigin.Current);
}
return false;
} Try / catch
try
{
using Image<Rgba32> img = Image.Load<Rgba32>(aniStream);
}
catch (InvalidImageContentException ex)
{
logger.LogError(ex, "ANI header cbSize is inconsistent with the anih chunk.");
} Prevention
- When serializing ANI headers, set cbSize to 36 (the fixed header size) explicitly.
- After resizing an 'anih' chunk with editing tools, rewrite the internal cbSize to match.
- Checksum cursor assets so header field corruption is detected before load.
When it happens
Trigger: Calling Decode/Identify where the parsed header's BytesInHeader is 0 or below 36, or exceeds the 'anih' chunk size — usually from writers that fill the field incorrectly or files whose header bytes were corrupted.
Common situations: Hand-written ANI generators that leave cbSize zeroed; files patched by tools that resized the 'anih' chunk without updating the internal cbSize; bit-rot flipping the size field.
Related errors
- The stream does not contain an ANI RIFF container.
- The ANI RIFF container size is invalid.
- The ANI file does not contain an animation header.
- The ANI animation header is truncated.
- The ANI file contains a truncated RIFF list.
AI-assisted analysis of SixLabors/ImageSharp@59ce6af6fc (2026-09-13).
Data as JSON: /api/errors/8de38edff822b3c8.
Report an issue: GitHub.
Appendix: source
Thrown at src/ImageSharp/Formats/Ani/AniDecoderCore.cs:315
/// <summary>
/// Parses the mandatory 36-byte ANI header and copies its observable values to image metadata.
/// </summary>
/// <param name="stream">The ANI stream.</param>
/// <param name="chunkSize">The ANI header chunk size.</param>
private void ReadAniHeader(BufferedReadStream stream, uint chunkSize)
{
if (chunkSize < AniHeader.Size)
{
throw new InvalidImageContentException("The ANI animation header is truncated.");
}
Span<byte> data = this.buffer;
ReadExactly(stream, data, "ANI header");
this.header = AniHeader.Parse(data);
if (this.header.BytesInHeader < AniHeader.Size || this.header.BytesInHeader > chunkSize)
{
throw new InvalidImageContentException("The ANI animation header declares an invalid size.");
}
this.aniMetadata.Width = this.header.Width;
this.aniMetadata.Height = this.header.Height;
this.aniMetadata.BitCount = this.header.BitCount;
this.aniMetadata.Planes = this.header.Planes;
this.aniMetadata.DisplayRate = this.header.DisplayRate;
this.aniMetadata.Flags = this.header.Flags;
}
/// <summary>
/// Reads a RIFF list type and records or parses its contents.
/// </summary>
/// <param name="stream">The ANI stream.</param>
/// <param name="listEnd">The exclusive end of the list payload.</param>
private void ReadList(BufferedReadStream stream, long listEnd)
{
if (listEnd - stream.Position < sizeof(uint))View on GitHub (pinned to 59ce6af6fc)