{"record":{"id":"44e833470405e889","repo":"arduino/Arduino","slug":"invalid-quoting-no-closing-escapingchar","errorCode":null,"errorMessage":"'Invalid quoting: no closing [' + escapingChar + '] char found.'","messagePattern":"'Invalid quoting: no closing \\[' \\+ escapingChar \\+ '\\] char found\\.'","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"arduino-core/src/processing/app/helpers/StringReplacer.java","lineNumber":121,"sourceCode":"        }\n\n        escapingChar = first;\n        i = i.substring(1);\n        escapedArg = \"\";\n      }\n\n      if (!i.endsWith(escapingChar)) {\n        escapedArg += i + \" \";\n        continue;\n      }\n\n      escapedArg += i.substring(0, i.length() - 1);\n      if (escapedArg.trim().length() != 0 || acceptEmptyArguments)\n        res.add(escapedArg);\n      escapingChar = null;\n    }\n    if (escapingChar != null)\n      throw new Exception(\"Invalid quoting: no closing [\" + escapingChar +\n          \"] char found.\");\n    return res.toArray(new String[0]);\n  }\n\n  public static String replaceFromMapping(String src, Map<String, String> map) {\n    return replaceFromMapping(src, map, \"{\", \"}\");\n  }\n\n  public static String replaceFromMapping(String src, Map<String, String> map,\n                                          String leftDelimiter,\n                                          String rightDelimiter) {\n    for (Map.Entry<String, String> entry : map.entrySet()) {\n      String keyword = leftDelimiter + entry.getKey() + rightDelimiter;\n      if (entry.getValue() != null && keyword != null) {\n          src = src.replace(keyword, entry.getValue());\n      }\n    }\n    return src;","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/arduino/Arduino/blob/a0df6e0e83b652c72bc78b0a1376c54d6ebc3bee/arduino-core/src/processing/app/helpers/StringReplacer.java#L103-L139","documentation":"StringReplacer.quotedSplit splits a formatted command string into arguments honoring quoting/escaping characters (e.g. quotes around paths with spaces). If the string ends while an escaping/quoting char is still open (no closing quote), it throws Exception(\"Invalid quoting: no closing [<char>] char found.\"). This guards against unbalanced quotes in recipes or command templates.","triggerScenarios":"Calling quotedSplit (directly or via formatAndSplit) on a string where a quote/escape character opened but never closed — e.g. a recipe value like \"\"{compiler.path}gcc -o \"{build.path}/out\"\" missing its final quote, or odd number of escaping chars.","commonSituations":"Hand-edited platform.txt recipes with unbalanced quotes; paths with quotes stripped or mangled by shell/JSON escaping; copy-paste of recipes losing a trailing quote; values containing unbalanced quote characters in filenames.","solutions":["Open the recipe/template string and balance the quotes around the reported character","Count quote characters in the offending pattern to find the odd one","Escape literal quote characters if they are part of a value rather than delimiters","Reinstall/restore the original platform.txt if the recipe was corrupted by editing"],"exampleFix":"// before\nrecipe.c.combine.pattern=\"{compiler.c.elf.cmd} {object_files} -o {build.path}/{build.project_name}.elf \" // trailing space ate the closing quote intent\n// after\nrecipe.c.combine.pattern=\"{compiler.c.elf.cmd} {object_files} -o {build.path}/{build.project_name}.elf\"","handlingStrategy":"validation","validationCode":"int count = 0;\nfor (char c : pattern.toCharArray()) if (c == '\"') count++;\nif (count % 2 != 0) throw new IllegalStateException(\"Unbalanced quotes in: \" + pattern);","typeGuard":null,"tryCatchPattern":"try {\n  String[] args = StringReplacer.formatAndSplit(src, dict);\n} catch (Exception e) {\n  if (e.getMessage().contains(\"Invalid quoting\"))\n    logger.error(\"Fix quotes in pattern: \" + src);\n  else throw e;\n}","preventionTips":["Count quotes when hand-editing recipes","Avoid quote characters inside filenames/paths","Test custom recipes with a dry run before real builds","Restore original recipes from the platform package after bad edits"],"tags":["quoting","string-parsing","arduino-build","recipe"],"backgroundTag":"invalid-argument-format","analyzedSha":"a0df6e0e83b652c72bc78b0a1376c54d6ebc3bee","analyzedAt":"2026-09-06T10:13:38.901Z","contentChangedAt":"2026-09-06T10:13:38.901Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}