dotnet/maui · error · BuildException
XC0060
XC0060
Error message
Markup expression not closed.
What it means
Thrown by ExpandMarkupsVisitor.ParseExpression when a markup expression string's last character is not '}'. Every XAML markup extension must be wrapped in { ... }; the compiler checks the closing brace first before attempting to parse.
Source
Thrown at src/Controls/src/Build.Tasks/ExpandMarkupsVisitor.cs:88
return false;
foreach (var kvp in parentElement.Properties)
{
if (kvp.Value != node)
continue;
name = kvp.Key;
return true;
}
return false;
}
static INode ParseExpression(ref string expression, ILContext context, IXmlNamespaceResolver nsResolver,
IXmlLineInfo xmlLineInfo)
{
if (expression.StartsWith("{}", StringComparison.Ordinal))
return new ValueNode(expression.Substring(2), null);
if (expression[expression.Length - 1] != '}')
throw new BuildException(BuildExceptionCode.MarkupNotClosed, xmlLineInfo, null);
if (!MarkupExpressionParser.MatchMarkup(out var match, expression, out var len))
throw new BuildException(BuildExceptionCode.MarkupParsingFailed, xmlLineInfo, null);
expression = expression.Substring(len).TrimStart();
if (expression.Length == 0)
throw new BuildException(BuildExceptionCode.MarkupNotClosed, xmlLineInfo, null);
var provider = new XamlServiceProvider(null, null);
provider.Add(typeof(ILContextProvider), new ILContextProvider(context));
provider.Add(typeof(IXmlNamespaceResolver), nsResolver);
provider.Add(typeof(IXmlLineInfoProvider), new XmlLineInfoProvider(xmlLineInfo));
return new MarkupExpansionParser().Parse(match, ref expression, provider);
}
class ILContextProvider(ILContext context)
{
public ILContext Context { get; } = context;View on GitHub (pinned to f377ff1c5e)
Solutions
- Ensure the expression ends with '}', e.g. "{Binding Path=Foo}".
- Use the XAML editor's validation/intellisense to auto-close braces.
- Search the file for '{' without a matching '}' to locate the broken expression.
Example fix
// before
<Label Text="{Binding Title" />
// after
<Label Text="{Binding Title}" /> Defensive patterns
Strategy: validation
Validate before calling
// Lint: every '{' markup expression must end with '}'
static bool MarkupIsClosed(string value) {
if (!value.Contains("{" + "") || value.StartsWith("{}")) return true;
return value.TrimEnd().EndsWith("}");
} Prevention
- Always close markup expressions with '}'.
- Use IDE intellisense to auto-close braces.
- Lint XAML for unmatched braces in CI.
When it happens
Trigger: Writing {Binding Path=Foo (missing closing brace); a stray truncation like Text="{x:Static"; an unterminated expression from a merge conflict or partial edit.
Common situations: Manual editing that deletes the trailing brace; templated/string-generated XAML that omits the brace; copy-paste that cut off the end.
Related errors
AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13).
Data as JSON: /api/errors/b4d7c5881825aa67.
Report an issue: GitHub.