d2phap/ImageGlass · error · InvalidOperationException

Can not decode WebP

Error message

Can not decode WebP

What it means

Thrown by WebPDecodeBGRAInto in ImageGlass.WebP when the native libwebp function returns NULL. The native API returns the output_buffer pointer on success and NULL on failure; the C# wrapper marshals it as Nullable<IntPtr> (WebPDecodeBGRAInto_x64) and converts null into this InvalidOperationException. This is the decode path used by WebPWrapper.Decode for 32-bpp ARGB (alpha-bearing) WebP images. Note the message says 'Can not' (two words), inconsistent with the BGR path which uses 'Cannot'.

Source

Thrown at v9/Components/ImageGlass.WebP/libwebp.cs:223

        if (WebPDecodeBGRInto_x64(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
            throw new InvalidOperationException("Cannot decode WebP");
    }
    [DllImport("libwebp.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeBGRInto")]
    private static extern IntPtr? WebPDecodeBGRInto_x64([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);




    /// <summary>Decode WEBP image pointed to by *data and returns BGRA samples into a preallocated buffer</summary>
    /// <param name="data">Pointer to WebP image data</param>
    /// <param name="data_size">This is the size of the memory block pointed to by data containing the image data</param>
    /// <param name="output_buffer">Pointer to decoded WebP image</param>
    /// <param name="output_buffer_size">Size of allocated buffer</param>
    /// <param name="output_stride">Specifies the distance between scan lines</param>
    internal static void WebPDecodeBGRAInto(IntPtr data, int data_size, IntPtr output_buffer, int output_buffer_size, int output_stride)
    {
        if (WebPDecodeBGRAInto_x64(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
            throw new InvalidOperationException("Can not decode WebP");
    }
    [DllImport("libwebp.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPDecodeBGRAInto")]
    private static extern IntPtr? WebPDecodeBGRAInto_x64([InAttribute()] IntPtr data, UIntPtr data_size, IntPtr output_buffer, int output_buffer_size, int output_stride);




    /// <summary>Decode WEBP image pointed to by *data and returns ARGB samples into a preallocated buffer</summary>
    /// <param name="data">Pointer to WebP image data</param>
    /// <param name="data_size">This is the size of the memory block pointed to by data containing the image data</param>
    /// <param name="output_buffer">Pointer to decoded WebP image</param>
    /// <param name="output_buffer_size">Size of allocated buffer</param>
    /// <param name="output_stride">Specifies the distance between scan lines</param>
    internal static void WebPDecodeARGBInto(IntPtr data, int data_size, IntPtr output_buffer, int output_buffer_size, int output_stride)
    {
        if (WebPDecodeARGBInto_x64(data, (UIntPtr)data_size, output_buffer, output_buffer_size, output_stride) == null)
            throw new InvalidOperationException("Can not decode WebP");
    }

View on GitHub (pinned to 4a3c4fecef)

Solutions

  1. Validate the WebP RIFF/WEBP signature and that the byte length is plausible for the declared dimensions before decoding.
  2. Ensure libwebp.dll matches the process architecture (x64) and the ABI version the wrapper was compiled against.
  3. Verify output_buffer_size >= stride * height and output_stride == BitmapData.Stride for the 32bpp bitmap.
  4. Decode from a private copy of the byte buffer so no other thread can mutate it while pinned.
  5. Route animated or feature-rich streams through the WebPDecoderConfig advanced decode path rather than BGRAInto.

Example fix

// before
if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
    LibWebp.WebPDecodeBGRInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);
else
    LibWebp.WebPDecodeBGRAInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);

// after: guard the input, then decode
if (rawWebP == null || rawWebP.Length < 12
    || rawWebP[0] != (byte)'R' || rawWebP[8] != (byte)'W')
{
    throw new ArgumentException("Not a WebP stream.");
}

if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
    LibWebp.WebPDecodeBGRInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);
else
    LibWebp.WebPDecodeBGRAInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);
Defensive patterns

Strategy: validation

Validate before calling

// Validate WebP signature + 32bpp ARGB buffer sizing before WebPDecodeBGRAInto
static void AssertDecodableBgra(byte[] rawWebP, IntPtr outputBuffer, int outputBufferSize, int outputStride, int width, int height)
{
    if (rawWebP == null || rawWebP.Length < 12
        || rawWebP[0] != (byte)'R' || rawWebP[3] != (byte)'F'
        || rawWebP[8] != (byte)'W' || rawWebP[11] != (byte)'P')
        throw new ArgumentException("Not a valid WebP stream.", nameof(rawWebP));
    if (outputBuffer == IntPtr.Zero)
        throw new ArgumentException("Output buffer is null.", nameof(outputBuffer));
    if (outputBufferSize < outputStride * height)
        throw new ArgumentException("Output buffer too small for 32bpp decode.", nameof(outputBufferSize));
}

Type guard

static bool IsWebP(byte[] data) =>
    data != null && data.Length >= 12
    && data[0] == (byte)'R' && data[1] == (byte)'I' && data[2] == (byte)'F' && data[3] == (byte)'F'
    && data[8] == (byte)'W' && data[9] == (byte)'E' && data[10] == (byte)'B' && data[11] == (byte)'P';

Try / catch

try
{
    using var bmp = new WebPWrapper().Decode(rawWebP);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("decode WebP"))
{
    logger.Warn(ex, "Alpha WebP decode failed; stream may be corrupt or truncated.");
    ShowUnsupportedImageError(path);
}

Prevention

When it happens

Trigger: Calling WebPWrapper.Decode (or LibWebp.WebPDecodeBGRAInto directly) for an alpha WebP whose bytes are truncated, corrupted, or not WebP; a data_size that does not match the real buffer length; an output_buffer too small for stride*height; an output_stride that disagrees with the locked BitmapData.Stride; or a libwebp.dll with an incompatible ABI version.

Common situations: A partially downloaded .webp is truncated mid-stream; a PNG/JPEG renamed to .webp is fed to the decoder; rawWebP is edited on another thread while pinned for decode; the deployed libwebp.dll has the wrong bitness or a newer/older ABI than the wrapper; or an animated WebP is decoded through the single-frame BGRAInto path instead of the animation decoder.

Related errors


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