d2phap/ImageGlass · error · InvalidOperationException

Cannot decode WebP

Error message

Cannot decode WebP

What it means

Thrown by WebPDecodeBGRInto in ImageGlass.WebP when the native libwebp function of the same name returns NULL. The C libwebp API returns the output_buffer pointer on success and NULL on failure, so a null result means libwebp could not decode the supplied bytes into the BGR buffer. The C# wrapper marshals that pointer as Nullable<IntPtr> (WebPDecodeBGRInto_x64) and converts the null case into this InvalidOperationException. It is the decode path used by WebPWrapper.Decode for 24-bpp (no-alpha) WebP images.

Source

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

    {
        return WebPGetInfo_x64(data, (UIntPtr)data_size, out width, out height);
    }
    [DllImport("libwebp.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "WebPGetInfo")]
    private static extern int WebPGetInfo_x64([InAttribute()] IntPtr data, UIntPtr data_size, out int width, out int height);




    /// <summary>Decode WEBP image pointed to by *data and returns BGR 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 WebPDecodeBGRInto(IntPtr data, int data_size, IntPtr output_buffer, int output_buffer_size, int output_stride)
    {
        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");
    }

View on GitHub (pinned to 4a3c4fecef)

Solutions

  1. Verify the bytes are a complete, valid WebP stream (bytes 0-3 are 'RIFF', bytes 8-11 are 'WEBP', chunk sizes consistent) before decoding.
  2. Confirm libwebp.dll matches the process architecture (x64 build needs the x64 dll) and is the same major/ABI version the wrapper targets.
  3. Check that output_buffer_size >= stride * height and that output_stride equals the BitmapData.Stride actually passed.
  4. Ensure the pinned rawWebP buffer is not modified on another thread during decode; decode from a defensive copy.
  5. If the stream may be animated or uses advanced features, route it through the WebPDecoderConfig advanced decode path instead of the single-frame BGRInto call.

Example fix

// before
LibWebp.WebPDecodeBGRInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);

// after: reject non-WebP / truncated streams before handing them to libwebp
static bool IsWebPSignature(byte[] b)
{
    return b != null && b.Length >= 12
        && b[0] == (byte)'R' && b[1] == (byte)'I' && b[2] == (byte)'F' && b[3] == (byte)'F'
        && b[8] == (byte)'W' && b[9] == (byte)'E' && b[10] == (byte)'B' && b[11] == (byte)'P';
}

if (!IsWebPSignature(rawWebP))
    throw new ArgumentException("Data is not a valid WebP stream.");

LibWebp.WebPDecodeBGRInto(ptrData, rawWebP.Length, bmpData.Scan0, outputSize, bmpData.Stride);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the WebP signature and buffer sizing before calling WebPDecodeBGRInto
static void AssertDecodableBgr(byte[] rawWebP, IntPtr outputBuffer, int outputBufferSize, int outputStride, int width, int height)
{
    if (rawWebP == null || rawWebP.Length < 12)
        throw new ArgumentException("WebP data is null or too short.", nameof(rawWebP));
    if (rawWebP[0] != (byte)'R' || rawWebP[1] != (byte)'I' || rawWebP[2] != (byte)'F' || rawWebP[3] != (byte)'F'
        || rawWebP[8] != (byte)'W' || rawWebP[9] != (byte)'E' || rawWebP[10] != (byte)'B' || rawWebP[11] != (byte)'P')
        throw new ArgumentException("Data does not have a WebP RIFF/WEBP signature.", nameof(rawWebP));
    if (outputBuffer == IntPtr.Zero)
        throw new ArgumentException("Output buffer is null.", nameof(outputBuffer));
    if (outputBufferSize < outputStride * height)
        throw new ArgumentException($"Output buffer ({outputBufferSize}) < stride*height ({outputStride * height}).", nameof(outputBufferSize));
}

Type guard

// Runtime guard over raw byte input before decode
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"))
{
    // Treat as an unsupported/corrupt image rather than a fatal error
    logger.Warn(ex, "WebP decode failed; file may be corrupt or not WebP.");
    ShowUnsupportedImageError(path);
}

Prevention

When it happens

Trigger: Calling WebPWrapper.Decode (or LibWebp.WebPDecodeBGRInto directly) with a byte[] that is truncated, corrupted, or not actually WebP; passing a data_size that disagrees with the real buffer length; an output_buffer too small for stride*height; an output_stride that does not match the locked BitmapData.Stride; or running against a libwebp.dll whose ABI/struct version differs from what the wrapper was compiled for.

Common situations: A .webp downloaded over an interrupted connection is truncated; a file mislabeled .webp is fed in; rawWebP is mutated on another thread while pinned; the wrong bitness of libwebp.dll (x86 vs x64) is deployed; or libwebp was upgraded without recompiling the wrapper so WEBP_DECODER_ABI_VERSION no longer matches.

Related errors


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