{"record":{"id":"cd21203cb945d1c5","repo":"dotnet/aspnetcore","slug":"the-type-0-does-not-have-an-associated-typecon-cd2120","errorCode":null,"errorMessage":"The type '{0}' does not have an associated TypeConverter that supports conversion from a string. Apply 'TypeConverterAttribute' to the type to register a converter.","messagePattern":"The type '(.+?)' does not have an associated TypeConverter that supports conversion from a string\\. Apply 'TypeConverterAttribute' to the type to register a converter\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/BindConverter.cs","lineNumber":2063,"sourceCode":"                    if (!elementParser(initialArray.GetValue(i), culture, out convertedArray[i]!))\n                    {\n                        value = default;\n                        return false;\n                    }\n                }\n\n                value = convertedArray;\n                return true;\n            }\n        }\n\n        [UnconditionalSuppressMessage(\"Trimming\", \"IL2026\", Justification = \"We expect unknown underlying types are configured by application code to be retained.\")]\n        private static BindParser<T> MakeTypeConverterConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>()\n        {\n            var typeConverter = TypeDescriptor.GetConverter(typeof(T));\n            if (typeConverter == null || !typeConverter.CanConvertFrom(typeof(string)))\n            {\n                throw new InvalidOperationException(\n                    $\"The type '{typeof(T).FullName}' does not have an associated {typeof(TypeConverter).Name} that supports \" +\n                    $\"conversion from a string. \" +\n                    $\"Apply '{typeof(TypeConverterAttribute).Name}' to the type to register a converter.\");\n            }\n\n            return ConvertWithTypeConverter;\n\n            bool ConvertWithTypeConverter(object? obj, CultureInfo? culture, out T value)\n            {\n                // We intentionally close-over the TypeConverter to cache it. The TypeDescriptor infrastructure is slow.\n                if (obj == null)\n                {\n                    value = default!;\n                    return true;\n                }\n                var converted = typeConverter.ConvertFrom(context: null, culture ?? CultureInfo.CurrentCulture, obj);\n                if (converted == null)\n                {","sourceCodeStart":2045,"sourceCodeEnd":2081,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/BindConverter.cs#L2045-L2081","documentation":"Thrown by BindConverter.MakeTypeConverterConverter when parsing user input back into type T (string -> T) and T has no TypeConverter supporting ConvertFrom(typeof(string)). This is the inbound counterpart of 236: Blazor needs a converter to turn the posted string into the bound property's type.","triggerScenarios":"Using @bind on a property of a custom type that lacks a TypeConverter for parsing from string, so when the user submits input Blazor cannot produce a T from the entered text.","commonSituations":"Custom value types/POCOs bound to form inputs without a registered converter; binding a wrapper type that has a ToString but no parse path.","solutions":["Add a TypeConverterAttribute with a converter that implements CanConvertFrom(string) and ConvertFrom.","Or bind a primitive property and map to the custom type on submit.","Ensure the converter handles the empty string (return default(T) or report an error) to avoid edge-case failures."],"exampleFix":"// before: no parse path for the custom type\npublic struct Money { public decimal Amount; }\n<InputNumber @bind-Value=\"model.Price\" />\n\n// after: TypeConverter implementing ConvertFrom\n[TypeConverter(typeof(MoneyConverter))]\npublic struct Money { public decimal Amount; public Money(decimal a){Amount=a;} }\n\npublic class MoneyConverter : TypeConverter\n{\n    public override bool CanConvertFrom(ITypeDescriptorContext? c, Type? s) => s == typeof(string);\n    public override object? ConvertFrom(ITypeDescriptorContext? c, CultureInfo? ci, object v)\n        => decimal.TryParse((string)v, NumberStyles.Currency, ci, out var d) ? new Money(d) : new Money(0m);\n    public override bool CanConvertTo(ITypeDescriptorContext? c, Type? t) => t == typeof(string);\n    public override object? ConvertTo(ITypeDescriptorContext? c, CultureInfo? ci, object? v, Type t)\n        => v is Money m ? m.Amount.ToString(\"C\", ci) : null;\n}","handlingStrategy":"type-guard","validationCode":"var converter = TypeDescriptor.GetConverter(typeof(T));\nif (converter == null || !converter.CanConvertFrom(typeof(string))) { /* add TypeConverter or bind a primitive */ }","typeGuard":"static bool CanParse<T>() { var c = TypeDescriptor.GetConverter(typeof(T)); return c != null && c.CanConvertFrom(typeof(string)); }","tryCatchPattern":null,"preventionTips":["Implement both ConvertTo and ConvertFrom in any custom TypeConverter.","Test parsing of empty strings and culture-specific formats.","Bind primitives where possible to avoid round-trip pitfalls."],"tags":["blazor","aspnetcore","binding","components","typeconverter"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}