{"record":{"id":"d4bd4dd12f5a922b","repo":"abpframework/abp","slug":"there-is-no-closing-char-for-an-opened-char","errorCode":null,"errorMessage":"There is no closing } char for an opened { char.","messagePattern":"There is no closing \\} char for an opened \\{ char\\.","errorType":"exception","errorClass":"FormatException","httpStatus":null,"severity":"error","filePath":"framework/src/Volo.Abp.Core/Volo/Abp/Text/Formatting/FormatStringTokenizer.cs","lineNumber":67,"sourceCode":"                    var dynamicValue = currentText.ToString();\n                    if (includeBracketsForDynamicValues)\n                    {\n                        dynamicValue = \"{\" + dynamicValue + \"}\";\n                    }\n\n                    tokens.Add(new FormatStringToken(dynamicValue, FormatStringTokenType.DynamicValue));\n                    currentText.Clear();\n\n                    break;\n                default:\n                    currentText.Append(c);\n                    break;\n            }\n        }\n\n        if (inDynamicValue)\n        {\n            throw new FormatException(\"There is no closing } char for an opened { char.\");\n        }\n\n        if (currentText.Length > 0)\n        {\n            tokens.Add(new FormatStringToken(currentText.ToString(), FormatStringTokenType.ConstantText));\n        }\n\n        return tokens;\n    }\n}\n","sourceCodeStart":49,"sourceCodeEnd":78,"githubUrl":"https://github.com/abpframework/abp/blob/7ed43b1931b9df46a50c0c59148a18645641d0df/framework/src/Volo.Abp.Core/Volo/Abp/Text/Formatting/FormatStringTokenizer.cs#L49-L78","documentation":"After the tokenizer loop finishes scanning every character, it checks whether inDynamicValue is still true. If so, an opening '{' was never closed by a '}', and it throws FormatException for the unterminated dynamic segment. Used by FormattedStringValueExtracter.Extract/IsMatch.","triggerScenarios":"Calling FormattedStringValueExtracter.Extract(str, format) with a format whose last dynamic segment has no closing brace: \"a{b\", \"name-{id\", or \"{unclosed\" (test case at FormattedStringTokenizer_Test.cs:13 tokenizes \"a sample { wrong format\").","commonSituations":"Truncated template strings; formats assembled by string concatenation that dropped the trailing '}; user-edited config that accidentally deleted a brace; copy-paste that cut off the end of a template.","solutions":["Close the dynamic segment: \"a{b}\" instead of \"a{b\".","Validate brace balance (equal counts and correct open/close ordering) before invoking Extract.","Catch FormatException around Extract for untrusted format input and treat it as a non-match."],"exampleFix":"// before: opening brace never closed\nvar r = FormattedStringValueExtracter.Extract(input, \"user-{id\");\n// throws FormatException: no closing } for the opened {\n\n// after: close the dynamic segment\nvar r = FormattedStringValueExtracter.Extract(input, \"user-{id}\");","handlingStrategy":"try-catch","validationCode":"static bool IsBraceBalanced(string format)\n{\n    if (format.IsNullOrEmpty()) return true;\n    var depth = 0;\n    foreach (var c in format)\n    {\n        if (c == '{') depth++;\n        else if (c == '}') { if (depth == 0) return false; depth--; }\n    }\n    return depth == 0; // false when an opening brace was never closed\n}\n\nif (!IsBraceBalanced(format)) { /* reject template */ }","typeGuard":null,"tryCatchPattern":"try\n{\n    result = FormattedStringValueExtracter.Extract(str, format);\n}\ncatch (FormatException)\n{\n    result = new ExtractionResult(false);\n}","preventionTips":["Validate brace balance for every template at load time.","Avoid truncating templates via Substring/concatenation that may drop a closing brace.","Store complete templates in one place and unit-test them.","Treat format strings as code: review and test them, do not assemble ad hoc at runtime."],"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"}