Yalantis/uCrop · error · CImgArgumentException

cimg::toc(): No previous call to 'cimg::tic()' has been made

Error message

cimg::toc(): No previous call to 'cimg::tic()' has been made.

What it means

cimg::toc() reads the most recent timestamp pushed by cimg::tic() from an internal stack to compute the elapsed time. If the stack position counter is 0 — i.e. tic() was never called, or toc() has already been called for every tic() — there is nothing to pop, so CImg throws CImgArgumentException instead of reading garbage data.

Source

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

    // Implement a tic/toc mechanism to display elapsed time of algorithms.
    inline cimg_uint64 tictoc(const bool is_tic) {
      cimg::mutex(2);
      static CImg<cimg_uint64> times(64);
      static unsigned int pos = 0;
      const cimg_uint64 t1 = cimg::time();
      if (is_tic) {
        // Tic.
        times[pos++] = t1;
        if (pos>=times._width)
          throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
        cimg::mutex(2,0);
        return t1;
      }

      // Toc.
      if (!pos)
        throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
      const cimg_uint64
        t0 = times[--pos],
        dt = t1>=t0?(t1 - t0):cimg::type<cimg_uint64>::max();
      const unsigned int
        edays = (unsigned int)(dt/86400000.),
        ehours = (unsigned int)((dt - edays*86400000.)/3600000.),
        emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.),
        esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.),
        ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.);
      if (!edays && !ehours && !emin && !esec)
        std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
                     cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
      else {
        if (!edays && !ehours && !emin)
          std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
                       cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
        else {
          if (!edays && !ehours)

View on GitHub (pinned to f788b534b4)

Solutions

  1. Verify each toc() is preceded by exactly one tic() on the same execution path; log or assert the tic/toc pairing during development.
  2. Remove or guard stray toc() calls (e.g. in destructors or catch blocks) so they only run when a tic() actually happened.
  3. Do not share tic()/toc() across threads; use per-thread timers (e.g. std::chrono::steady_clock) if measurement happens concurrently.
  4. If the tic() may be skipped conditionally, skip the matching toc() with the same condition so counts stay balanced.

Example fix

// before
void run(bool skip) {
  if (!skip) cimg::tic();
  do_work();
  cimg::toc(); // throws when skip==true because tic() was never called
}
// after
void run(bool skip) {
  if (!skip) cimg::tic();
  do_work();
  if (!skip) cimg::toc(); // balanced with tic()
}
Defensive patterns

Strategy: validation

Validate before calling

// Track whether a tic() is outstanding before calling toc()
static bool ticActive = false;
#define SAFE_TIC() do { cimg::tic(); ticActive = true; } while(0)
#define SAFE_TOC() do { if (!ticActive) { fprintf(stderr,"toc without tic\n"); } else { cimg::toc(); ticActive = false; } } while(0)

Try / catch

try {
  cimg::toc();
} catch (CImgArgumentException& e) {
  std::cerr << "toc() without tic(): " << e.what() << "\n";
  // recover: re-instrument or ignore the stray timing call
}

Prevention

When it happens

Trigger: Calling cimg::toc() before any cimg::tic(), calling toc() more times than tic(), or calling toc() after an earlier unbalanced sequence already consumed the stack (e.g. toc() invoked from a different thread or after an exception path skipped the tic()).

Common situations: Copy-pasted toc() leftover from removed tic() instrumentation; calling toc() in a destructor/error handler while tic() was only called on the happy path; mixing tic/toc across threads (the stack is per-process static state); mismatched tic/toc counts in instrumented libraries.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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