Skip to main content

How to Resolve Error "CS0181: Attribute constructor parameter has type, which is not a valid attribute parameter type" in C#

The Compiler Error CS0181 is a restriction error related to Attributes. The message reads: "Attribute constructor parameter 'ParameterName' has type 'Type', which is not a valid attribute parameter type".

Attributes in C# (like [Obsolete], [Serializable], or custom attributes) are metadata that is baked directly into the compiled assembly (DLL or EXE). Because this data must be serialized and stored permanently in the assembly's metadata tables at compile-time, the compiler restricts the types of data you can pass to an attribute constructor. You cannot pass complex objects, dynamic values, or types that the metadata format does not support (like decimal or DateTime).

This guide explains which types are allowed and how to work around the restrictions for unsupported types.

Understanding Valid Attribute Parameter Types

The Common Language Runtime (CLR) restricts attribute parameters to a specific list of types that can be easily serialized into the metadata.

Allowed Types:

  • Primitive Types: bool, byte, char, short, int, long, float, double.
  • String: string.
  • System.Type: typeof(MyClass).
  • Enums: Provided the underlying type is one of the integers above.
  • Object: object (However, the value passed must still be one of the types above).
  • Arrays: One-dimensional arrays of any of the types above.

Forbidden Types (Trigger CS0181):

  • decimal (This is a struct, not a primitive in metadata terms).
  • DateTime / TimeSpan.
  • Guid.
  • Any Custom Class or Struct.

Scenario 1: The decimal Limitation

This is the most common source of this error. Developers often need to annotate fields with financial values and naturally reach for decimal. However, decimal is not a valid attribute parameter type.

Example of error

Defining an attribute constructor that accepts a decimal.

using System;

public class PriceAttribute : Attribute
{
public decimal Amount { get; }

// ⛔️ Error CS0181: Attribute constructor parameter 'amount' has type 'decimal',
// which is not a valid attribute parameter type.
public PriceAttribute(decimal amount)
{
Amount = amount;
}
}

Solution: Use double or string

Since you cannot change the CLR rules, you must change your data type.

Option A: Use Double (If precision allows)

public class PriceAttribute : Attribute
{
public double Amount { get; }

// ✅ Correct: 'double' is a valid attribute parameter type.
public PriceAttribute(double amount)
{
Amount = amount;
}
}

[Price(99.50)]
public class Product { }

Option B: Use String (For exact precision) Pass the value as a string and parse it at runtime (e.g., via Reflection).

public class PriceAttribute : Attribute
{
public decimal Amount { get; }

// ✅ Correct: Pass "99.99" as a string. Parse it inside.
public PriceAttribute(string amount)
{
if (decimal.TryParse(amount, out decimal result))
{
Amount = result;
}
}
}

[Price("99.99")]
public class Product { }

Scenario 2: Passing DateTime or Guid

Like decimal, DateTime and Guid are structs that cannot be embedded in attribute metadata constants.

Example of error

using System;

public class ExpiryAttribute : Attribute
{
// ⛔️ Error CS0181: 'DateTime' is not valid here.
public ExpiryAttribute(DateTime expiryDate)
{
}
}

Solution: Use ISO 8601 Strings

Pass the date as a string in a standard format (like "yyyy-MM-dd") or use long ticks.

using System;

public class ExpiryAttribute : Attribute
{
public DateTime ExpiryDate { get; }

// ✅ Correct: Pass date as string, parse immediately
public ExpiryAttribute(string dateString)
{
ExpiryDate = DateTime.Parse(dateString);
}
}

[Expiry("2025-12-31")]
public class License { }

Scenario 3: Passing Custom Objects

You cannot pass an instance of a custom class (new User()) into an attribute. Attributes are static metadata; they cannot instantiate dynamic objects during the declaration.

Example of error

public class UserRole 
{
public string Name;
}

public class AuthorizeAttribute : Attribute
{
// ⛔️ Error CS0181: 'UserRole' is a custom class. Not valid.
public AuthorizeAttribute(UserRole role)
{
}
}

Solution: Pass Types or Enums

Instead of passing an instance of the class, pass the Type or an Enum representing the configuration.

Option A: Use typeof

public class AuthorizeAttribute : Attribute
{
// ✅ Correct: 'Type' is a valid parameter
public AuthorizeAttribute(Type roleType)
{
}
}

[Authorize(typeof(UserRole))]
public class AdminPanel { }

Option B: Use Enums

public enum Roles { Admin, Guest }

public class AuthorizeAttribute : Attribute
{
// ✅ Correct: Enums are valid
public AuthorizeAttribute(Roles role)
{
}
}

[Authorize(Roles.Admin)]
public class AdminPanel { }

Conclusion

CS0181 acts as a gatekeeper for metadata compatibility.

  1. Check the Type: Is your attribute constructor accepting decimal, DateTime, or a custom class?
  2. Convert to Primitives: Change the parameter to string, int, or double.
  3. Parse Later: Use the constructor logic to convert that primitive (e.g., the string) back into the complex type (e.g., the decimal) needed for your logic.