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

  1. Ensure the header's cbSize field equals the 'anih' chunk size (normally 36) — patch or regenerate the header.
  2. Re-export the ANI with a conformant tool so cbSize and chunk size agree.
  3. 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 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


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)