d2phap/ImageGlass · error · InvalidDataException

IGE: Native codec '{CodecId}' returned an invalid pixel buff

Error message

IGE: Native codec '{CodecId}' returned an invalid pixel buffer.

What it means

Thrown by NativeCodecProxy.WrapPluginBufferAsImage when the IGPixelBuffer returned by a plugin fails basic validation: Data is null, or Width/Height is zero or negative. The host refuses to hand such a buffer to Skia because SKImage.FromPixels on a null/empty buffer is undefined behavior.

Source

Thrown at source/ImageGlass.Lib/Plugins/NativeCodecProxy.cs:800

        public void Dispose()
        {
            if (Frame.OwnsImage) Frame.Image.Dispose();
        }
    }


    /// <summary>
    /// Wraps a plugin-owned <see cref="IGPixelBuffer"/> in an <see cref="SKImage"/>
    /// without copying the pixels. The returned image owns the plugin buffer:
    /// disposing the image calls back into the plugin's <c>FreePixelBuffer</c>
    /// via the release delegate (which the SDK contract requires to be thread-safe).
    /// </summary>
    internal SKImage WrapPluginBufferAsImage(in IGPixelBuffer buffer, SKColorSpace? srcColorSpace)
    {
        // Validate the buffer before we hand it to Skia.
        if (buffer.Data == null || buffer.Width <= 0 || buffer.Height <= 0)
        {
            throw new InvalidDataException(
                $"IGE: Native codec '{CodecId}' returned an invalid pixel buffer.");
        }

        var (colorType, alphaType) = MapPixelFormat((IGPixelFormat)buffer.PixelFormat);
        if (colorType == SKColorType.Unknown)
        {
            throw new InvalidDataException(
                $"IGE: Native codec '{CodecId}' returned an unsupported pixel format ({buffer.PixelFormat}).");
        }

        var info = new SKImageInfo(buffer.Width, buffer.Height, colorType, alphaType);
        if (srcColorSpace is not null)
        {
            info = info.WithColorSpace(srcColorSpace);
        }

        // Carrier holds the plugin codec API + a copy of the buffer descriptor so
        // SkiaSharp can hand the pointer back to the plugin on dispose.

View on GitHub (pinned to 4a3c4fecef)

Solutions

  1. Report the bug to the plugin author: the plugin returned IGStatus.OK with an empty/null pixel buffer, which violates the SDK contract.
  2. Rebuild the plugin against the current SDK and verify its DecodeStaticRaster populates IGPixelBuffer.Data, .Width, .Height, .Stride on the success path.
  3. Disable the plugin for the affected format and let a built-in codec handle the file.
  4. If authoring a plugin, add an assertion at the end of DecodeStaticRaster that the buffer is fully populated before returning IGStatus.OK.

Example fix

// before
if (buffer.Data == null || buffer.Width <= 0 || buffer.Height <= 0)
    throw new InvalidDataException($"IGE: Native codec '{CodecId}' returned an invalid pixel buffer.");

// after — include the bad fields so the plugin author can pinpoint the unset slot
if (buffer.Data == null || buffer.Width <= 0 || buffer.Height <= 0)
    throw new InvalidDataException(
        $"IGE: Native codec '{CodecId}' returned an invalid pixel buffer " +
        $"(Data={(buffer.Data == null ? "null" : "ok")}, Width={buffer.Width}, Height={buffer.Height}).");
Defensive patterns

Strategy: validation

Validate before calling

// Validate the plugin-returned buffer before passing it to Skia.
static bool IsValidPluginBuffer(in IGPixelBuffer b) =>
    b.Data != null && b.Width > 0 && b.Height > 0;

Type guard

static bool IsValidPluginBuffer(in IGPixelBuffer b) =>
    b.Data != null && b.Width > 0 && b.Height > 0;

Try / catch

try { image = proxy.WrapPluginBufferAsImage(in buffer, colorSpace); }
catch (InvalidDataException ex) when (ex.Message.Contains("invalid pixel buffer"))
{ _failureManager.RecordSoftFailure(proxy.Plugin.PluginId, "empty pixel buffer on OK"); return BuiltInFallback(metadata); }

Prevention

When it happens

Trigger: Produced at NativeCodecProxy.cs:800 inside WrapPluginBufferAsImage, called from DecodeCore after a plugin's DecodeStaticRaster reported IGStatus.OK but filled the IGPixelBuffer with a null Data pointer or non-positive dimensions. The plugin lied: it returned success but did not produce a usable buffer.

Common situations: Plugin bug: decode succeeded internally but the buffer struct was not populated (forgot to set Data, or set Width/Height from a zero-sized frame); plugin returned a metadata-only frame; plugin's static decode path is incomplete and zeroed the struct on success; ABI mismatch where Width/Height land at wrong offsets.

Related errors


AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13). Data as JSON: /api/errors/e46d190aaf1359e3. Report an issue: GitHub.