SixLabors/ImageSharp · error · ImageFormatException

ICO and CUR resources cannot contain more than 65535…

Error message

ICO and CUR resources cannot contain more than 65535 directory entries.

What it means

Thrown during icon encoding when more than ushort.MaxValue (65535) directory entries are supplied. The ICO/CUR ICONDIR stores Count as a 16-bit value, so SixLabors.ImageSharp cannot represent more entries and throws ImageFormatException before writing anything.

Solutions

  1. Reduce the number of encoded frames to 65535 or fewer (typically icons have 1–20 entries)
  2. Validate entries.Length before encoding and split or truncate the set
  3. Use a different container format if you genuinely need many frames
  4. Catch ImageFormatException and report the limit to the user

Example fix

// before
using var imageCollection = Image.LoadFrames(source); // could exceed 65535
imageCollection.Save("out.ico");
// after
using var imageCollection = Image.LoadFrames(source);
if (imageCollection.Frames.Count > 65535)
    throw new InvalidOperationException("ICO supports at most 65535 entries");
imageCollection.Save("out.ico");
Defensive patterns

Strategy: validation

Validate before calling

// before saving, enforce the ICONDIR 16-bit entry limit
if ((uint)frames.Count > ushort.MaxValue)
    throw new InvalidOperationException($"ICO supports at most {ushort.MaxValue} entries, got {frames.Count}");

Try / catch

try
{
    image.Save(stream, new IcoEncoder());
}
catch (ImageFormatException ex)
{
    // >65535 entries requested
    throw new InvalidOperationException("Too many frames for ICO", ex);
}

Prevention

When it happens

Trigger: Calling the icon encoder (Save as ICO/CUR, Image.Save with IcoEncoder) with an image collection or metadata provider yielding more than 65535 frames/entries — practically only via programmatic batch encoding.

Common situations: Programmatic icon generation loops that accidentally encode thousands of frames; using the ICO encoder on a large animated image's frames instead of a real multi-format-capable encoder; off-by-scale bugs in entry collection.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of SixLabors/ImageSharp@59ce6af6fc (2026-09-13). Data as JSON: /api/errors/712363db5b026e76. Report an issue: GitHub.

Appendix: source

Thrown at src/ImageSharp/Formats/Icon/IconEncoderCore.cs:86

    /// <summary>
    /// Encodes a contiguous source-frame range using a stack-only metadata provider.
    /// </summary>
    /// <typeparam name="TPixel">The source pixel type.</typeparam>
    /// <typeparam name="TProvider">The metadata provider type.</typeparam>
    /// <param name="image">The source image.</param>
    /// <param name="stream">The destination stream.</param>
    /// <param name="frameIndex">The first source-frame index.</param>
    /// <param name="entries">The directory metadata for the source frames.</param>
    /// <param name="provider">The frame metadata provider.</param>
    /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
    internal void Encode<TPixel, TProvider>(Image<TPixel> image, Stream stream, int frameIndex, Span<EncodingFrameMetadata> entries, TProvider provider, CancellationToken cancellationToken)
        where TPixel : unmanaged, IPixel<TPixel>
        where TProvider : struct, IEncodingFrameMetadataProvider
    {
        if ((uint)entries.Length > ushort.MaxValue)
        {
            throw new ImageFormatException("ICO and CUR resources cannot contain more than 65535 directory entries.");
        }

        // Offsets stored in ICO/CUR entries are relative to the start of this child resource, not the containing ANI stream.
        long basePosition = stream.Position;
        IconDir fileHeader = new(this.iconFileType, (ushort)entries.Length);

        // Reserve the directory first because BytesInRes and ImageOffset are known only after each payload is encoded.
        int dataOffset = IconDir.Size + (IconDirEntry.Size * entries.Length);
        _ = stream.Seek(dataOffset, SeekOrigin.Current);

        for (int i = 0; i < entries.Length; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();

            // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data
            // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft.
            ImageFrame<TPixel> frame = image.Frames[frameIndex + i];

View on GitHub (pinned to 59ce6af6fc)