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
- Verify the bytes are a complete, valid WebP stream (bytes 0-3 are 'RIFF', bytes 8-11 are 'WEBP', chunk sizes consistent) before decoding.
- Confirm libwebp.dll matches the process architecture (x64 build needs the x64 dll) and is the same major/ABI version the wrapper targets.
- Check that output_buffer_size >= stride * height and that output_stride equals the BitmapData.Stride actually passed.
- Ensure the pinned rawWebP buffer is not modified on another thread during decode; decode from a defensive copy.
- 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
- Validate the RIFF/WEBP signature before handing bytes to libwebp.
- Ship the exact bitness of libwebp.dll that matches your process (x64).
- Keep the libwebp native version in lockstep with the ImageGlass.WebP wrapper's ABI constant.
- Decode from a private byte[] copy so the pinned buffer cannot be mutated mid-decode.
- For animated WebP, use the WebPDecoderConfig advanced path, not the single-frame BGRInto call.
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
- Can not decode WebP
- WebPInitDecoderConfig failed. Wrong version?
- Failed WebPGetFeatures with error {result}
- Crop options exceeded WebP image dimensions
- Failed WebPDecode with error {result}
AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13).
Data as JSON: /api/errors/ddbc4f38e6811339.
Report an issue: GitHub.