Yalantis/uCrop · error · CImgArgumentException
[" cimg_appname "_math_parser] CImg<%s>::%s: Unterminated st
Error message
[" cimg_appname "_math_parser] CImg<%s>::%s: Unterminated string literal, in expression '%s'.
What it means
CImg's math expression parser (used by functions like fill() with an expression string, or expression-based image operations) scans the expression for string literals delimited by quotes. If the parser reaches the end of input while still inside a quoted string (mode != 0), it throws CImgArgumentException reporting an unterminated string literal, truncating the expression to 64 chars via strellipsize for readability.
Source
Thrown at ucrop/src/main/jni/CImg.h:25016
int _level = 0;
for (const char *ps = _expr._data; *ps && _level>=0; ++ps) {
if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
if (!is_escaped && *ps=='\'') { // Non-escaped character
if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
}
*(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1):
*ps=='(' || *ps=='['?_level++:
*ps==')' || *ps==']'?--_level:
_level);
mode = next_mode;
is_escaped = next_is_escaped;
next_is_escaped = false;
}
if (mode) {
cimg::strellipsize(_expr,64);
throw CImgArgumentException("[" cimg_appname "_math_parser] "
"CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
pixel_type(),_cimg_mp_calling_function,
_expr._data);
}
if (_level) {
cimg::strellipsize(_expr,64);
throw CImgArgumentException("[" cimg_appname "_math_parser] "
"CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
pixel_type(),_cimg_mp_calling_function,
_expr._data);
}
return res;
}
// Find and return index of current image 'imgin' within image list 'imglist'.
unsigned int get_mem_img_index() {
if (mem_img_index==~0U) {
if (&imgout>=imglist.data() && &imgout<imglist.end())View on GitHub (pinned to f788b534b4)
Solutions
- Inspect the expression string at runtime (log it) and find the opening quote that is never closed
- Ensure every string literal inside the expression has a matching closing quote of the same character
- If the expression passes through a shell, JSON, or XML layer, verify quotes survive escaping (double them or use the layer's escape syntax)
- Split the expression into smaller pieces and test each in isolation to locate the malformed literal
Example fix
// before
img.fill("if(x>2,'odd',0)"); // fine
img.fill("if(x>2,'odd,0)"); // unterminated string literal
// after
img.fill("if(x>2,'odd',0)"); Defensive patterns
Strategy: validation
Validate before calling
// C++: check quote balance before passing expression to CImg
bool quotes_balanced(const std::string& e) {
int sq = 0, dq = 0;
for (size_t i = 0; i < e.size(); ++i) {
if (i > 0 && e[i-1] == '\\') continue; // skip escaped
if (e[i] == '\'') sq ^= 1;
if (e[i] == '"') dq ^= 1;
}
return sq == 0 && dq == 0;
}
if (!quotes_balanced(expr)) throw std::invalid_argument("unterminated string literal in expression"); Type guard
bool has_balanced_quotes(const std::string& e); // see validationCode
Try / catch
try {
img.fill(expr);
} catch (const cimg_library::CImgArgumentException& e) {
std::cerr << "Bad expression: " << e.what() << " | expr=" << expr << std::endl;
} Prevention
- Log the final expression string before every CImg call
- Never build quoted literals inside expressions by hand-concatenation; use a helper
- Beware shell/JSON layers stripping quotes; test with a fixed literal first
When it happens
Trigger: Passing a math expression string to a CImg function (e.g. img.fill("...") or an expression in a formula-based call) where an opening quote (' or ") is never closed, e.g. fill("if(x>2,'hello',0)") with a missing closing quote, or a quote accidentally escaped/eaten by an outer shell or JSON layer.
Common situations: Building expressions programmatically or in shell scripts where quotes get stripped by the shell or a JSON/ini config layer; typos in G'MIC-style expressions; copy-pasting expressions where smart quotes or one of a matched pair is lost.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- [" cimg_appname "_math_parser] CImg<%s>::%s: Unbalanced pare
- [cimg_appname_math_parser] CImg<%s>::%s: %s: Too much specif
- [cimg_appname_math_parser] CImg<%s>::%s: %s: %s name specifi
- [cimg_appname_math_parser] CImg<%s>::%s: %s: Invalid assignm
- [cimg_appname_math_parser] CImg<%s>::%s: %s: Invalid %slvalu
AI-assisted analysis of Yalantis/uCrop@f788b534b4 (2026-09-08).
Data as JSON: /api/errors/b9f8260ff1ab0e87.
Report an issue: GitHub.