Yalantis/uCrop · error · CImgArgumentException

save_jxl(): JPEG XL only supports at most 4 channels.

Error message

save_jxl(): JPEG XL only supports at most 4 channels.

What it means

JPEG XL encoding in CImg supports at most 4 channels (gray, RGB, RGB+alpha, RGBA). save_jxl() throws CImgArgumentException when the image's _spectrum exceeds 4, before any encoder setup, because libjxl's basic info cannot represent that channel count.

Solutions

  1. Reduce channels before saving: img.channels(0,3) to keep the first 4, or split into multiple images.
  2. Pack channels differently (e.g. save each band as a separate grayscale JXL file).
  3. Choose a format that supports more channels (e.g. TIFF) instead of JXL.

Example fix

// before
img.save_jxl("spectral.jxl"); // img.spectrum() == 8
// after
CImg<T> vis = img.get_channels(0, 0, 0, 0, 0, 3); // keep RGB-like subset
vis.save_jxl("spectral.jxl");
Defensive patterns

Strategy: validation

Validate before calling

if (img.spectrum() > 4) {
  img.channels(0, 3); // keep first 4 channels before jxl save
}
img.save_jxl("out.jxl");

Prevention

When it happens

Trigger: Calling save_jxl() on an image whose _spectrum > 4, e.g. multispectral/hyperspectral data, CMYK+extra channels, or images produced by concatenating many channel planes.

Common situations: Scientific imaging and remote-sensing pipelines where images carry 5+ spectral bands, then get saved to JXL for compression without channel reduction.

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/00c6f90fda63131d. Report an issue: GitHub.

Appendix: source

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

    /**
      \param filename Filename, as a C-string.
      \param distance Sets the level for lossy compression: lower = higher quality.
        Range: 0 .. 25. 0.0 = mathematically lossless
      \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
    **/
    const CImg<T>& save_jxl(const char *const filename, const float distance=1.0f,
                            const unsigned int bytes_per_pixel=0) const {
      return _save_jxl(filename,distance,bytes_per_pixel);
    }

    const CImg<T>& _save_jxl(const char *const filename, const float distance=1.0f,
                             const unsigned int bytes_per_pixel=0) const {
      if (!filename)
        throw CImgArgumentException(_cimg_instance
                                    "save_jxl(): Specified filename is (null).",
                                    cimg_instance);
      if (_spectrum > 4)
        throw CImgArgumentException(_cimg_instance
                                    "save_jxl(): JPEG XL only supports at most 4 channels.",
                                    cimg_instance);
      double stmin, stmax = (double)max_min(stmin);
      if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
        cimg::warn(_cimg_instance
                   "save_jxl(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
                   cimg_instance,
                   stmin,stmax,filename);
      if (bytes_per_pixel>2 || sizeof(T)<bytes_per_pixel)
        throw CImgArgumentException(_cimg_instance
                                    "save_jxl(): bytes_per_pixel must be in [0, 2] and less than or equal to sizeof(T)",
                                    cimg_instance);
      if (_depth>1)
        cimg::warn(_cimg_instance
                   "save_jxl(): Instance is volumetric, only the first slice will be saved in file '%s'.",
                   cimg_instance,
                   filename);
#ifndef cimg_use_jxl

View on GitHub (pinned to f788b534b4)