Yalantis/uCrop · error · CImgArgumentException

remove(): Invalid remove request at positions %u->%u.

Error message

remove(): Invalid remove request at positions %u->%u.

What it means

CImgList::remove(pos1,pos2) throws when the normalized start position npos1 is greater than or equal to the list size _width, meaning the entire requested removal range lies past the end of the list. Nothing can be removed, so the library raises CImgArgumentException with the normalized position range.

Source

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

    //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
    template<typename t>
    CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
                           const bool is_shared=false) const {
      return (+*this).insert(n,list,pos,is_shared);
    }

    //! Remove all images between from indexes.
    /**
      \param pos1 Starting index of the removal.
      \param pos2 Ending index of the removal.
    **/
    CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
      const unsigned int
        npos1 = pos1<pos2?pos1:pos2,
        tpos2 = pos1<pos2?pos2:pos1,
        npos2 = tpos2<_width?tpos2:_width - 1;
      if (npos1>=_width)
        throw CImgArgumentException(_cimglist_instance
                                    "remove(): Invalid remove request at positions %u->%u.",
                                    cimglist_instance,
                                    npos1,tpos2);
      else {
        if (tpos2>=_width)
          throw CImgArgumentException(_cimglist_instance
                                      "remove(): Invalid remove request at positions %u->%u.",
                                      cimglist_instance,
                                      npos1,tpos2);

        for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
        const unsigned int nb = 1 + npos2 - npos1;
        if (!(_width-=nb)) return assign();
        if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation
          if (npos1!=_width)
            std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
          std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
        } else { // Removing items with reallocation

View on GitHub (pinned to f788b534b4)

Solutions

  1. Check list.size() and skip the call when pos1 >= size() before removing
  2. Clamp positions: pos1 = min(pos1, size-1), pos2 = min(pos2, size-1)
  3. Verify the range wasn't already removed (guard against double removal)

Example fix

// before
list.remove(4, 6); // throws when list.size() <= 4
// after
if (list.size() > 4) list.remove(4, std::min(6u, list.size() - 1));
Defensive patterns

Strategy: validation

Validate before calling

if (list.is_empty() || std::min(pos1,pos2) >= list.size()) return; // skip out-of-range removal

Try / catch

try { list.remove(pos1, pos2); } catch (const CImgArgumentException& e) { /* log and skip */ }

Prevention

When it happens

Trigger: list.remove(pos1,pos2) where min(pos1,pos2) >= list.size(), e.g. removing indices 4->6 from a 4-element list, or calling remove on an empty list.

Common situations: Reusing cached index ranges after the list shrank; removing a batch twice (second call is out of range); calling on an empty list built from a failed load.

Related errors


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