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
- Ensure quantizer.BuildPalette (or QuantizerUtilities.BuildPalette) is called before consuming the palette
- If implementing a custom IFrameQuantizer, set the palette in BuildPalette and only use it after
- 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
- Call BuildPalette (or ExecuteQuantization) before accessing any palette-dependent quantization result
- When implementing IFrameQuantizer, assign the palette inside BuildPalette and document the required order
- Prefer QuantizerUtilities.ExecuteQuantization over manual two-phase calls
- Add a debug assertion or unit test verifying palette non-emptiness before quantize
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)