Yalantis/uCrop · error · CImgException

load_tiff(): Failed to allocate memory

Error message

load_tiff(): Failed to allocate memory (%s) for file '%s'.

What it means

In the RGBA fast path, load_tiff() allocates a raster buffer of nx*ny*sizeof(cimg_uint32) bytes with _TIFFmalloc. If that allocation returns null, the library throws a CImgException naming the requested buffer size, meaning the system could not supply that much memory.

Solutions

  1. Free memory or reduce concurrent allocations before loading.
  2. Downscale or crop the image before load (e.g. preprocess with tiffcp -Z or vips).
  3. Load the image on a device/process with a larger memory budget (64-bit).
  4. Use the tile/strip read path instead of the RGBA path by changing the file's storage layout.

Example fix

// before: 40000x40000 RGBA => ~6.4 GB raster
img.load_tiff("huge_scan.tif");
// after: downscale first
// $ tiffcp -Z 8000 huge_scan.tif small.tif
img.load_tiff("small.tif");
Defensive patterns

Strategy: validation

Validate before calling

long long need = (long long)width * height * 4;
if (need > availableMemoryBudget()) throw std::runtime_error("TIFF too large for memory budget");

Try / catch

try { img.load_tiff(path); }
catch (CImgException &e) { if (strstr(e.what(), "allocate memory")) downscaleThenReload(path); else throw; }

Prevention

When it happens

Trigger: Loading a very large TIFF (huge width*height) such that nx*ny*4 bytes exceeds available memory, e.g. a multi-gigapixel scan on a memory-constrained Android device.

Common situations: Mobile/JNI environments with low per-process memory limits, 32-bit address-space exhaustion, or 32-bit overflow of nx*ny*sizeof() for gigantic dimensions.

Related errors


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

Appendix: source

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

        voxel_size[1] = 1.f/voxel_size[1];
      }
      if (description) {
        const char *s_description = 0;
        if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
          CImg<charT>::string(s_description).move_to(*description);
      }
      const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
      assign(nx,ny,1,spectrum);

      if ((photo>=3 && sampleformat==1 &&
           (bitspersample==4 || bitspersample==8) &&
           (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
          (bitspersample==1 && samplesperpixel==1)) {
        // Special case for unsigned color images.
        cimg_uint32 *const raster = (cimg_uint32*)_TIFFmalloc(nx*ny*sizeof(cimg_uint32));
        if (!raster) {
          _TIFFfree(raster); TIFFClose(tif);
          throw CImgException(_cimg_instance
                              "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
                              cimg_instance,
                              cimg::strbuffersize(nx*ny*sizeof(cimg_uint32)),filename);
        }
        TIFFReadRGBAImage(tif,nx,ny,raster,0);
        switch (spectrum) {
        case 1 :
          cimg_forXY(*this,x,y)
            (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
          break;
        case 3 :
          cimg_forXY(*this,x,y) {
            (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
            (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
            (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
          }
          break;
        case 4 :

View on GitHub (pinned to f788b534b4)