Yalantis/uCrop · error · CImgIOException

save_jxl(): Failed to set lossy compression level when savin

Error message

save_jxl(): Failed to set lossy compression level when saving file '%s'.

What it means

For lossy JXL, CImg sets the compression distance via JxlEncoderSetFrameDistance. A non-success return makes save_jxl() tear down the encoder and throw CImgIOException, meaning libjxl rejected the requested distance value (e.g. out of its accepted range) or the encoder is in a bad state.

Source

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

      }

      JxlColorEncoding colorEncoding = {};
      JXL_BOOL isGray = pixelFormat.num_channels<3?JXL_TRUE:JXL_FALSE;
      JxlColorEncodingSetToSRGB(&colorEncoding, isGray);
      if (JXL_ENC_SUCCESS!=JxlEncoderSetColorEncoding(encoder,&colorEncoding)) {
        cimg::fclose(file);
        JxlEncoderDestroy(encoder);
        throw CImgIOException(_cimg_instance
                              "save_jxl(): Failed to set color encoding when saving file '%s'.",
                              cimg_instance,
                              filename);
      }

      JxlEncoderFrameSettings* frameSettings = JxlEncoderFrameSettingsCreate(encoder,NULL);
      if (JXL_ENC_SUCCESS!=JxlEncoderSetFrameDistance(frameSettings,distance)) {
        cimg::fclose(file);
        JxlEncoderDestroy(encoder);
        throw CImgIOException(_cimg_instance
                              "save_jxl(): Failed to set lossy compression level when saving file '%s'.",
                              cimg_instance,
                              filename);
      }
      if (JXL_ENC_SUCCESS!=JxlEncoderAddImageFrame(frameSettings,&pixelFormat,(const void*)rgbBuffer._data,
                                                   rgbBuffer.size())) {
        cimg::fclose(file);
        JxlEncoderDestroy(encoder);
        throw CImgIOException(_cimg_instance
                              "save_jxl(): Failed to set image data when saving file '%s'.",
                              cimg_instance,
                              filename);
      }

      CImg<ucharT> alphaBuffer;
      if (hasAlpha) {
        ucharT *alphaPtr = (ucharT*)ptr_a;
        if (sizeof(T)!=bytesPerPixel) {

View on GitHub (pinned to f788b534b4)

Solutions

  1. Use a valid distance in [0, 25]: 0 for lossless, ~1.0 for visually lossless.
  2. Convert a JPEG-style quality q to distance, e.g. distance = q >= 100 ? 0 : (100 - q) / 10.0f, before calling save_jxl.
  3. Clamp/validate the distance parameter at the call site.

Example fix

// before
int quality = 85;
img.save_jxl("out.jxl", (float)quality); // 85 is out of JXL distance range
// after
float distance = quality >= 100 ? 0.0f : (100.0f - quality) / 10.0f;
img.save_jxl("out.jxl", distance);
Defensive patterns

Strategy: validation

Validate before calling

float dist = quality >= 100 ? 0.0f : (100.0f - quality) / 10.0f;
if (dist < 0.0f || dist > 25.0f) {
  throw std::invalid_argument("jxl distance must be within [0, 25]");
}
img.save_jxl("out.jxl", dist);

Prevention

When it happens

Trigger: Calling save_jxl(path, distance) with a distance value libjxl rejects — JXL distance is typically 0 (lossless) up to ~25; passing a negative value, NaN, or an out-of-range number triggers this.

Common situations: Users porting JPEG quality (1–100) directly into the JXL distance parameter, or config files supplying quality as a 0–100 scale that is passed unconverted to distance.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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