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

  1. Change the attribute usage to pass a single string or typeof(Type) constructor argument, e.g. [ContentProperty("Text")] instead of a non-string value.
  2. Add a string-based constructor to the custom attribute and keep the typed overload only for direct (non-parser) use.
  3. If the attribute legitimately needs zero args, ensure the caller passes zeroArgsAllowed/noArgs appropriately (as CPA does).
  4. 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

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


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)