Yalantis/uCrop · error · CImgIOException

load_jxl(): Failed to decode image data '%s'.

Error message

load_jxl(): Failed to decode image data '%s'.

What it means

On JXL_DEC_NEED_IMAGE_OUT_BUFFER, load_jxl() asks libjxl for the required output size via JxlDecoderImageOutBufferSize. A non-success return makes allocating the output impossible, so the decoder is destroyed and this error is thrown. Usually reflects an unsupported pixel format choice or decoder state problem.

Source

Thrown at ucrop/src/main/jni/CImg.h:57207

                                  nfilename);
          }
          if (jxlInfo.have_animation!=0) {
            JxlDecoderDestroy(decoder);
            throw CImgIOException(_cimg_instance
                                  "load_jxl(): Does not support animated JPEG XL '%s'.",
                                  cimg_instance,
                                  nfilename);
          }
          hasAlpha = jxlInfo.alpha_bits!=0;
          nChannels = hasAlpha?jxlInfo.num_color_channels + 1:jxlInfo.num_color_channels;
          isGray = jxlInfo.num_color_channels==1;
        } else if (status==JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
          std::size_t imgDataSize = 0;
          format.num_channels = nChannels;
          format.data_type = jxlInfo.bits_per_sample==16?JXL_TYPE_UINT16:JXL_TYPE_UINT8;
          if (JXL_DEC_SUCCESS!=JxlDecoderImageOutBufferSize(decoder,&format,&imgDataSize)) {
            JxlDecoderDestroy(decoder);
            throw CImgIOException(_cimg_instance
                                  "load_jxl(): Failed to decode image data '%s'.",
                                  cimg_instance,
                                  nfilename);
          }
          imgData.assign(imgDataSize);
          if (JXL_DEC_SUCCESS!=JxlDecoderSetImageOutBuffer(decoder,&format,(void*)imgData.data(),imgDataSize)) {
            JxlDecoderDestroy(decoder);
            throw CImgIOException(_cimg_instance
                                  "load_jxl(): Failed to set decode buffer '%s'.",
                                  cimg_instance,
                                  nfilename);
          }
        }
      }

      assign(jxlInfo.xsize,jxlInfo.ysize,1,nChannels);
      T
        *ptr_r = data(0,0,0,0),

View on GitHub (pinned to f788b534b4)

Solutions

  1. Check the image's bits_per_sample/num_color_channels; re-encode to standard 8/16-bit RGB(A) JXL.
  2. Update CImg/libjxl; newer libjxl accepts more format combinations.
  3. Decode with libjxl directly choosing a supported JxlPixelFormat, then wrap the buffer in a CImg.

Example fix

// before
// 16-bit float CMYK jxl -> ImageOutBufferSize fails
// after
// re-encode: cjxl input.png -- BitsPerSample=8 out.jxl
img.load_jxl("out.jxl");
Defensive patterns

Strategy: validation

Validate before calling

bool jxlFormatSupported(unsigned bits_per_sample, unsigned num_color_channels, unsigned alpha_bits) {
  unsigned n = num_color_channels + (alpha_bits ? 1 : 0);
  return (bits_per_sample == 8 || bits_per_sample == 16) && n >= 1 && n <= 4;
}

Try / catch

try { img.load_jxl(path); } catch (CImgIOException& e) { log("unsupported jxl pixel format"); reencodeToStdJxl(path); }

Prevention

When it happens

Trigger: JxlDecoderImageOutBufferSize(decoder, &format, &imgDataSize) returns != JXL_DEC_SUCCESS where format.num_channels was set from image basic info and data_type is UINT8/UINT16 by bits_per_sample.

Common situations: Images with unusual sample layouts (e.g. >4 channels, CMYK, float samples, 32-bit depth) that clash with the hardcoded UINT8/UINT16 format choices in CImg.

Understand the failure class

Related errors


AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08). Data as JSON: /api/errors/4269231ac337f32d. Report an issue: GitHub.