Yalantis/uCrop · warning

load_tiff(): File '%s' contains %u image(s) while specified

Error message

load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).

What it means

load_tiff() emits this cimg::warn (non-fatal) when the requested frame range does not fit the actual number of images/directories stored in the TIFF file. The library counts the TIFF directories and, if nfirst_frame is beyond the last frame (or nlast_frame is), warns and then clamps nlast_frame or returns an empty image. It indicates the load will not return what was asked for.

Source

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

#ifndef cimg_use_tiff
      cimg::unused(bits_per_value,voxel_size,description);
      if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
        throw CImgArgumentException(_cimg_instance
                                    "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
                                    cimg_instance,
                                    filename);
      return load_other(filename);
#else
#if cimg_verbosity<3
      TIFFSetWarningHandler(0);
      TIFFSetErrorHandler(0);
#endif
      TIFF *tif = TIFFOpen(filename,"r");
      if (tif) {
        unsigned int nb_images = 0;
        do ++nb_images; while (TIFFReadDirectory(tif));
        if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
          cimg::warn(_cimg_instance
                     "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
                     cimg_instance,
                     filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);

        if (nfirst_frame>=nb_images) return assign();
        if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
        TIFFSetDirectory(tif,0);
        CImg<T> frame;
        for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
          frame._load_tiff(tif,l,bits_per_value,voxel_size,description);
          if (l==nfirst_frame)
            assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
          if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
            resize(std::max(frame._width,_width),
                   std::max(frame._height,_height),-100,
                   std::max(frame._spectrum,_spectrum),0);
          draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
        }

View on GitHub (pinned to f788b534b4)

Solutions

  1. Check the frame count first (e.g. via CImg::load_tiff with a probe or a TIFF library call) and clamp nfirst_frame/nlast_frame to nb_images-1.
  2. Use ~0U (default) for nlast_frame instead of a hardcoded count so the library clamps automatically.
  3. Remember frame indices are 0-based; subtract 1 from any 1-based index.
  4. Re-export the source TIFF with the expected number of pages if the data itself is wrong.

Example fix

// before
img.load_tiff("scan.tif", 0, 9); // assumes 10 pages
// after
unsigned int nb = 0; { TIFF* t = TIFFOpen("scan.tif","r"); do ++nb; while (TIFFReadDirectory(t)); TIFFClose(t); }
img.load_tiff("scan.tif", 0, nb ? nb-1 : ~0U);
Defensive patterns

Strategy: validation

Validate before calling

unsigned int nb = 0;
{ /* count TIFF directories before loading */ }
if (nfirst >= nb || (nlast != ~0U && nlast >= nb)) clamp_or_throw();

Type guard

bool frameRangeValid(unsigned first, unsigned last, unsigned nb) { return first < nb && (last == ~0U || last < nb); }

Prevention

When it happens

Trigger: Calling CImg::load_tiff(filename, nfirst_frame, nlast_frame, nstep_frame) where nb_images counted via TIFFReadDirectory is smaller than the requested range: nfirst_frame>=nb_images, or nlast_frame!=~0U and nlast_frame>=nb_images.

Common situations: Hardcoded frame indices after the source TIFF was regenerated with fewer pages; multi-page scans truncated; off-by-one assuming frames are 1-based when load_tiff is 0-based; iterating a directory of TIFFs with varying page counts.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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