dotnet/wpf · error · ArgumentException
SR.ParserAttributeArgsLow
Error message
SR.ParserAttributeArgsLow
What it means
ReflectionHelper.GetCustomAttributeData extracts a single string (or, when allowTypeAlso, typeof(Type)) constructor argument from a CustomAttributeData for the expected attribute type. If the single argument is neither a string nor a Type (attrValue remains null), or an attribute that requires an argument has zero arguments, it throws ArgumentException(SR.ParserAttributeArgsLow, attrType.Name) — i.e. the attribute was used with fewer/simpler constructor arguments than this reflection helper supports.
Solutions
- Change the attribute usage to pass a single string or typeof(Type) constructor argument, e.g. [ContentProperty("Text")] instead of a non-string value.
- Add a string-based constructor to the custom attribute and keep the typed overload only for direct (non-parser) use.
- If the attribute legitimately needs zero args, ensure the caller passes zeroArgsAllowed/noArgs appropriately (as CPA does).
- Update the referenced attribute assembly so the constructor signature matches what the WPF XAML parser expects.
Example fix
// before
public class MyContentAttribute : Attribute { public MyContentAttribute(int index) {...} }
[MyContent(3)] // ParserAttributeArgsLow at parse time
// after
public class MyContentAttribute : Attribute { public MyContentAttribute(string name) {...} }
[MyContent("Content")] Defensive patterns
Strategy: validation
Validate before calling
// Audit custom attributes consumed by the XAML parser: single string or typeof(Type) ctor arg only
bool parserCompatible(ConstructorInfo ctor) =>
ctor.GetParameters().Length == 1 &&
(ctor.GetParameters()[0].ParameterType == typeof(string) || ctor.GetParameters()[0].ParameterType == typeof(Type)); Type guard
static bool IsValidParserAttributeArg(object arg) => arg is string || arg is Type;
Try / catch
try { data = ReflectionHelper.GetCustomAttributeData(type, attrType, out typeValue, allowTypeAlso); }
catch (ArgumentException ex) { log.Error($"Attribute {attrType.Name} used with unsupported constructor arguments: {ex.Message}"); } Prevention
- Design parser-consumed attributes with string or Type constructor parameters
- Test custom attributes through reflection-only XAML parsing early
- Avoid enum/int/bool constructor arguments on attributes read by the WPF parser
When it happens
Trigger: An attribute consumed by the XAML parser (e.g. ContentPropertyAttribute, XamlSetMarkupExtensionAttribute-style lookups, friend-assembly checks) is applied with an argument type other than string/Type — e.g. [Attr(42)] or [Attr(EnumValue.X)] — or with no constructor argument when one is required.
Common situations: Custom markup/CPA attributes authored with non-string constructor parameters (int, enum, bool) that WPF's reflection-only parser cannot interpret; attribute assemblies built with different constructor overloads; reflection-only load paths where typed arguments aren't resolved.
Understand the failure class
Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.
Related errors
- SR.CanOnlyHaveOneChild
- SR.CanOnlyHaveOneChild
- SR.Format(SR.MarkupWriter_CannotSerializeNestedPublictype…
- SR.Format(SR.MarkupWriter_CannotSerializeNonPublictype…
- SR.Format(SR.ParserPrefixNSProperty, nsPrefix, nameString)
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/ea18c7485f83e646.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs:347
// "test" is the Constructor Argument
IList<CustomAttributeTypedArgument> constructorArguments = cad.ConstructorArguments;
if (constructorArguments.Count == 1 && !noArgs)
{
CustomAttributeTypedArgument tca = constructorArguments[0];
attrValue = tca.Value as string;
#if PBTCOMPILER
if (attrValue == null && allowTypeAlso && tca.ArgumentType == GetMscorlibType(typeof(Type)))
#else
if (attrValue is null && allowTypeAlso && tca.ArgumentType == typeof(Type))
#endif
{
typeValue = tca.Value as Type;
attrValue = typeValue.AssemblyQualifiedName;
}
if (attrValue is null)
{
throw new ArgumentException(SR.Format(SR.ParserAttributeArgsLow, attrType.Name));
}
}
else if (constructorArguments.Count == 0)
{
// zeroArgsAllowed = true for CPA for example.
// CPA with no args is valid and would mean that this type is overriding a base CPA
if (noArgs || zeroArgsAllowed)
{
attrValue = string.Empty;
}
else
{
throw new ArgumentException(SR.Format(SR.ParserAttributeArgsLow, attrType.Name));
}
}
else
{
throw new ArgumentException(SR.Format(SR.ParserAttributeArgsHigh, attrType.Name));View on GitHub (pinned to 81131a70a4)