SixLabors/ImageSharp · error · InvalidOperationException

Frame Quantizer palette has not been built.

Error message

Frame Quantizer palette has not been built.

What it means

CheckPaletteState is a preconditions helper called before quantization output is written; it requires that the frame quantizer's palette has already been produced by a prior BuildPalette step. An empty palette means the two-phase quantization protocol was not followed, so the library cannot map pixels. It throws InvalidOperationException to indicate a caller sequencing bug rather than bad input data.

Solutions

  1. Ensure quantizer.BuildPalette (or QuantizerUtilities.BuildPalette) is called before consuming the palette
  2. If implementing a custom IFrameQuantizer, set the palette in BuildPalette and only use it after
  3. Use QuantizerUtilities.ExecuteQuantization to run both steps in the correct order instead of calling phases manually

Example fix

// before
var quantizer = new OctreeQuantizer();
var quantized = quantizer.QuantizeFrame(image.Frames[0], out var palette); // palette never built
// after
using var quantizer = new OctreeQuantizer();
using var paletteBuilt = quantizer.BuildPalette(image.Frames[0]);
var quantized = quantizer.QuantizeFrame(image.Frames[0], paletteBuilt, out var palette);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure palette is built before consuming:
// var palette = quantizer.BuildPalette(frame); // non-empty ReadOnlyMemory<TPixel>
// Then call quantize with the built palette.

Type guard

static bool HasPalette<TPixel>(ReadOnlyMemory<TPixel> p) where TPixel : unmanaged, IPixel<TPixel> => !p.IsEmpty;

Try / catch

try
{
    // palette-consuming quantization call
}
catch (InvalidOperationException ex) when (ex.Message.Contains("palette has not been built"))
{
    // call BuildPalette first, then retry
    throw;
}

Prevention

When it happens

Trigger: Calling AddFrameToPalette or palette-consuming quantization APIs with a quantizer whose palette memory is empty, i.e. skipping ExecuteQuantization's palette build phase or calling the second phase first.

Common situations: Implementing a custom IFrameQuantizer that forgets to assign the palette; calling quantizer methods out of order when writing custom encoders; refactorings that removed the BuildPalette call before quantization.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs:160

        }
    }

    /// <summary>
    /// Helper method for throwing an exception when a frame quantizer palette has
    /// been requested but not built yet.
    /// </summary>
    /// <typeparam name="TPixel">The pixel format.</typeparam>
    /// <param name="palette">The frame quantizer palette.</param>
    /// <exception cref="InvalidOperationException">
    /// The palette has not been built via <see cref="IQuantizer{TPixel}.AddPaletteColors(in Buffer2DRegion{TPixel})"/>
    /// </exception>
    [MethodImpl(InliningOptions.ColdPath)]
    public static void CheckPaletteState<TPixel>(in ReadOnlyMemory<TPixel> palette)
        where TPixel : unmanaged, IPixel<TPixel>
    {
        if (palette.IsEmpty)
        {
            throw new InvalidOperationException("Frame Quantizer palette has not been built.");
        }
    }

    /// <summary>
    /// Execute both steps of the quantization.
    /// </summary>
    /// <param name="quantizer">The pixel specific quantizer.</param>
    /// <param name="source">The source image frame to quantize.</param>
    /// <param name="bounds">The bounds within the frame to quantize.</param>
    /// <typeparam name="TPixel">The pixel type.</typeparam>
    /// <returns>
    /// A <see cref="IndexedImageFrame{TPixel}"/> representing a quantized version of the source frame pixels.
    /// </returns>
    public static IndexedImageFrame<TPixel> BuildPaletteAndQuantizeFrame<TPixel>(
        this IQuantizer<TPixel> quantizer,
        ImageFrame<TPixel> source,
        Rectangle bounds)
        where TPixel : unmanaged, IPixel<TPixel>

View on GitHub (pinned to 59ce6af6fc)