dotnet/maui · error · BuildException

XC0128

XC0128

Error message

A key is required in {StaticResource}.

What it means

Thrown by the compiled {StaticResource} provider when no Key can be found on the markup node. StaticResource requires a Key attribute (or a single collection item used as the key); if neither is present, keyNode is null or not a ValueNode and the build fails.

Source

Thrown at src/Controls/src/Build.Tasks/CompiledMarkupExtensions/StaticResourceExtension.cs:24

using Mono.Cecil.Cil;
using static Mono.Cecil.Cil.Instruction;
using static Mono.Cecil.Cil.OpCodes;

namespace Microsoft.Maui.Controls.Build.Tasks
{
	//yes, this is a ICompiledMarkupExtension, but declared as ICompiledValueProvider so it's evaluated later (in SetPropertyValue, not CreateObject)
	class StaticResourceExtension : ICompiledValueProvider
	{
		public IEnumerable<Instruction> ProvideValue(VariableDefinitionReference vardefref, ModuleDefinition module, BaseNode node, ILContext context)
		{
			var name = new XmlName("", "Key");
			var eNode = node as ElementNode;

			if (!eNode.Properties.TryGetValue(name, out INode keyNode) && eNode.CollectionItems.Any())
				keyNode = eNode.CollectionItems[0];

			if (!(keyNode is ValueNode keyValueNode))
				throw new BuildException(BuildExceptionCode.StaticResourceSyntax, eNode as IXmlLineInfo, null, null);

			var n = eNode;
			while (n != null)
			{
				if (n.Properties.TryGetValue(new XmlName(XamlParser.MauiUri, "Resources"), out var resourcesNode))
				{
					//single resource in <Resources>
					if (resourcesNode is ElementNode irn
						&& irn.Properties.TryGetValue(XmlName.xKey, out INode xKeyNode)
						&& context.Variables.TryGetValue(irn, out VariableDefinition value)
						&& xKeyNode is ValueNode xKeyValueNode
						&& xKeyValueNode.Value as string == keyValueNode.Value as string)
					{
						if (value.VariableType.FullName == "System.String")
						{
							foreach (var instruction in TryConvert(irn.CollectionItems[0] as ValueNode, eNode, vardefref, module, context))
								yield return instruction;
							yield break;

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Add the Key attribute with the resource name: {StaticResource Key="MyBrush"} or the shorthand form.
  2. Ensure the Key is a literal string value, not a nested markup expression.
  3. Confirm the matching resource with that key exists in a reachable ResourceDictionary.
  4. If you need runtime key resolution, switch off XAML compilation for that binding (x:CompileBindings) or use a code-behind lookup.

Example fix

// before
<ContentPage.Resources>
  <!-- resource defined -->
</ContentPage.Resources>
<Label Text="{StaticResource}" />
// after
<Label Text="{StaticResource Key=GreetingTemplate}" />
Defensive patterns

Strategy: validation

Validate before calling

// Lint: every {StaticResource} must carry a literal Key
static bool StaticResourceHasKey(string expr) {
    if (!expr.StartsWith("{StaticResource")) return true; // not our concern
    return expr.Contains("Key=") ||
           Regex.IsMatch(expr, @"\{StaticResource\s+([A-Za-z_][\w]*)");
}

Prevention

When it happens

Trigger: Writing <StaticResource /> with no Key attribute and no inline content; binding the Key to a non-literal node (e.g. another markup extension) so keyNode is not a ValueNode; referencing {StaticResource} where the resource key is dynamically computed.

Common situations: Forgetting to specify which resource to fetch; confusing StaticResource with DynamicResource syntax; copy-paste leaving an empty {StaticResource}; trying to key on a binding expression which the compiled form cannot evaluate at build time.

Related errors


AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13). Data as JSON: /api/errors/393658520c844a6f. Report an issue: GitHub.