Yalantis/uCrop · error · CImgArgumentException

[cimg_appname_math_parser] CImg<%s>::%s: Call stack overflow

Error message

[cimg_appname_math_parser] CImg<%s>::%s: Call stack overflow (infinite recursion?), in expression '%s'.

What it means

During compilation of a math expression, the parser enforces a recursion depth limit of 256 nested call levels. Exceeding it means the expression recurses infinitely (e.g. a macro calling itself without a terminating condition), so CImgArgumentException is thrown with an excerpt of the offending expression.

Source

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

        need_input_copy(mp.need_input_copy),
        result(mem._data + (mp.result - mp.mem._data)),
        result_end(mp.result_end?mem._data + (mp.result_end - mp.mem._data):0),
        rng((cimg::_rand(),cimg::rng())),calling_function(0) {

#if cimg_use_openmp!=0
        mem[_cimg_mp_slot_t] = (double)omp_get_thread_num();
        rng+=omp_get_thread_num();
#endif
        opcode.assign();
        opcode._is_shared = true;
      }

      // Compilation procedure.
      unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
                           unsigned char block_flags) {
        if (depth>256) {
          cimg::strellipsize(expr,64);
          throw CImgArgumentException("[" cimg_appname "_math_parser] "
                                      "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
                                      "in expression '%s'.",
                                      pixel_type(),_cimg_mp_calling_function,
                                      (ss - 4)>expr._data?ss - 4:expr._data);
        }
        char c1, c2;

        // Simplify expression when possible.
        do {
          c2 = 0;
          if (ss<se) {
            while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss; // Remove leading blanks and ';'
            while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; // Remove trailing blanks and ';'
          }
          while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Remove useless start/end parentheses
            ++ss; --se; c2 = 1;
          }
          if (*ss=='_' && ss + 1<se && ss[1]=='(') { // Remove leading '_(something)' comment

View on GitHub (pinned to f788b534b4)

Solutions

  1. Add a termination/base case to the recursive macro (use conditionals like if(cond, base, rec)).
  2. Rewrite the recursion as an iterative loop construct (while()/for() in the expression language) or compute the value outside CImg.
  3. Check for accidental self-reference/cyclic references between macro names.
  4. Test complex expressions in a small tool (e.g. the CImg 'math' evaluator) before embedding them in the app.

Example fix

// before
"fact(n) : fact(n-1)*n" // infinite recursion
// after
"fact(n) : if(n<=1,1,fact(n-1)*n)" // base case stops recursion
Defensive patterns

Strategy: validation

Validate before calling

// static check before evaluation: reject known-unbounded recursive macros
if (expr_contains_unbounded_recursion(expr)) return ERR_INFINITE_RECURSION;
img.eval(expr);

Try / catch

try {
  img.eval(expr);
} catch (const CImgArgumentException& e) {
  if (strstr(e.what(), "Call stack overflow")) {
    // mark expression as recursive/broken; disable feature or prompt user
  }
}

Prevention

When it happens

Trigger: Defining a macro that calls itself unconditionally (e.g. foo() : foo()+1) or deeply/nested self-referencing expressions; mutual recursion between macros (a calls b, b calls a); extremely deep nesting of function calls beyond 256 levels.

Common situations: Translating recursive formulas (factorial, fractals) into CImg macros without a base case; copy-pasted expressions from other DSLs with unbounded recursion.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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