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
- Verify each toc() is preceded by exactly one tic() on the same execution path; log or assert the tic/toc pairing during development.
- Remove or guard stray toc() calls (e.g. in destructors or catch blocks) so they only run when a tic() actually happened.
- Do not share tic()/toc() across threads; use per-thread timers (e.g. std::chrono::steady_clock) if measurement happens concurrently.
- 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
- Guard stray toc() calls in destructors/error handlers so they only run if tic() actually happened.
- Keep tic/toc pairs in the same function/scope so matching is obvious.
- Never share tic()/toc() across threads; the stack is static shared state.
- Count tic/toc invocations in debug builds and assert equality at shutdown.
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
- cimg::tic(): Too much calls to 'cimg::tic()' without calls t
- "[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()
- crop(): Empty instance.
- cimg::SDL3_attr(): %s
- cimg::mod(): Specified modulo value is 0.
AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08).
Data as JSON: /api/errors/be30435dc49fa22b.
Report an issue: GitHub.