{"record":{"id":"0a8961081b942ad8","repo":"abpframework/abp","slug":"incorrect-syntax-at-char-i-format-string-can-no","errorCode":null,"errorMessage":"Incorrect syntax at char {i}! format string can not contain nested dynamic value expression!","messagePattern":"Incorrect syntax at char (.+?)! format string can not contain nested dynamic value expression!","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"framework/src/Volo.Abp.Core/Volo/Abp/Text/Formatting/FormatStringTokenizer.cs","lineNumber":24,"sourceCode":"\ninternal class FormatStringTokenizer\n{\n    public List<FormatStringToken> Tokenize(string format, bool includeBracketsForDynamicValues = false)\n    {\n        var tokens = new List<FormatStringToken>();\n\n        var currentText = new StringBuilder();\n        var inDynamicValue = false;\n\n        for (var i = 0; i < format.Length; i++)\n        {\n            var c = format[i];\n            switch (c)\n            {\n                case '{':\n                    if (inDynamicValue)\n                    {\n                        throw new FormatException(\"Incorrect syntax at char \" + i + \"! format string can not contain nested dynamic value expression!\");\n                    }\n\n                    inDynamicValue = true;\n\n                    if (currentText.Length > 0)\n                    {\n                        tokens.Add(new FormatStringToken(currentText.ToString(), FormatStringTokenType.ConstantText));\n                        currentText.Clear();\n                    }\n\n                    break;\n                case '}':\n                    if (!inDynamicValue)\n                    {\n                        throw new FormatException(\"Incorrect syntax at char \" + i + \"! These is no opening brackets for the closing bracket }.\");\n                    }\n\n                    inDynamicValue = false;","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/abpframework/abp/blob/7ed43b1931b9df46a50c0c59148a18645641d0df/framework/src/Volo.Abp.Core/Volo/Abp/Text/Formatting/FormatStringTokenizer.cs#L6-L42","documentation":"FormatStringTokenizer.Tokenize splits a format string into ConstantText and DynamicValue tokens, where dynamic segments are delimited by { and }. When a '{' character is encountered while already inside a dynamic segment (inDynamicValue == true), the tokenizer throws FormatException because nested dynamic placeholders are unsupported. This tokenizer powers FormattedStringValueExtracter.Extract, which reverses string.Format to pull named values out of a target string (e.g. format \"My name is {name}.\" matched against \"My name is Neo.\").","triggerScenarios":"Calling FormattedStringValueExtracter.Extract(str, format) (or IsMatch) with a format containing an opening brace inside an already-open dynamic segment: \"a{b{c}}\", \"{x{y}}\", or \"{0{1}}\" (the test case at FormattedStringTokenizer_Test.cs:14). Any second '{' before the matching '}' triggers it.","commonSituations":"User-supplied or config-driven format templates that accidentally nest placeholders; copying a string.Format numeric-index style (\"{0}\") into a template and then embedding another token; feeding route-like patterns containing literal braces without realizing braces are always interpreted as dynamic delimiters (there is no escape sequence).","solutions":["Flatten the format so each dynamic segment contains only its name and no inner braces: \"a{b}\" instead of \"a{b{c}}\".","If a literal brace is needed in constant text, that is unsupported by this tokenizer — keep braces out of constant text and restructure the template.","Validate the format string with a brace-balance/nesting check before calling Extract, and reject malformed templates at config load time.","Wrap Extract in a try/catch(FormatException) to degrade gracefully when the format comes from an untrusted source."],"exampleFix":"// before: nested braces inside a dynamic segment\nvar r = FormattedStringValueExtracter.Extract(input, \"doc-{id{rev}}.pdf\");\n// throws FormatException at the inner '{'\n\n// after: single, non-nested dynamic segment\nvar r = FormattedStringValueExtracter.Extract(input, \"doc-{id}.pdf\");","handlingStrategy":"try-catch","validationCode":"static bool IsValidFormatString(string format)\n{\n    if (format.IsNullOrEmpty()) return true;\n    var depth = 0;\n    foreach (var c in format)\n    {\n        if (c == '{') { if (depth > 0) return false; depth++; }\n        else if (c == '}') { if (depth == 0) return false; depth--; }\n    }\n    return depth == 0;\n}\n\n// usage:\nif (!IsValidFormatString(format)) { /* reject or sanitize */ }","typeGuard":null,"tryCatchPattern":"ExtractionResult result;\ntry\n{\n    result = FormattedStringValueExtracter.Extract(str, format, ignoreCase);\n}\ncatch (FormatException ex)\n{\n    logger.LogDebug(ex, \"Malformed format string: {Format}\", format);\n    result = new ExtractionResult(false);\n}","preventionTips":["Treat format templates as code: validate them at load time, not at first user request.","Remember braces are always dynamic delimiters in this tokenizer — there is no escape; keep literal braces out of templates.","Prefer named single-segment placeholders ({name}) and avoid any nesting.","Keep formats in one place (config/constants) so they can be unit-tested with representative inputs."],"tags":["formatting","format-string","parsing","tokenization","formatted-string"],"backgroundTag":null,"analyzedSha":"7ed43b1931b9df46a50c0c59148a18645641d0df","analyzedAt":"2026-08-13T16:26:11.351Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}