{"record":{"id":"1cc91aa6d428df9a","repo":"dotnet/aspnetcore","slug":"the-type-0-does-not-have-an-associated-typecon","errorCode":null,"errorMessage":"The type '{0}' does not have an associated TypeConverter that supports conversion to a string. Apply 'TypeConverterAttribute' to the type to register a converter.","messagePattern":"The type '(.+?)' does not have an associated TypeConverter that supports conversion to 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":1848,"sourceCode":"                {\n                    builder.Append(\", \\\"\");\n                    builder.Append(JsonEncodedText.Encode(elementFormatter(value[i], culture)?.ToString() ?? string.Empty).Value);\n                    builder.Append('\\\"');\n                }\n\n                builder.Append(']');\n\n                return builder.ToString();\n            }\n        }\n\n        [UnconditionalSuppressMessage(\"Trimming\", \"IL2026\", Justification = \"We expect unknown underlying types are configured by application code to be retained.\")]\n        private static BindFormatter<T> MakeTypeConverterFormatter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>()\n        {\n            var typeConverter = TypeDescriptor.GetConverter(typeof(T));\n            if (typeConverter == null || !typeConverter.CanConvertTo(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 to a string. \" +\n                    $\"Apply '{typeof(TypeConverterAttribute).Name}' to the type to register a converter.\");\n            }\n\n            return FormatWithTypeConverter;\n\n            string? FormatWithTypeConverter(T value, CultureInfo? culture)\n            {\n                // We intentionally close-over the TypeConverter to cache it. The TypeDescriptor infrastructure is slow.\n                return typeConverter.ConvertToString(context: null, culture ?? CultureInfo.CurrentCulture, value);\n            }\n        }\n    }\n\n    internal static class ParserDelegateCache\n    {\n        private static readonly ConcurrentDictionary<Type, Delegate> _cache = new ConcurrentDictionary<Type, Delegate>();","sourceCodeStart":1830,"sourceCodeEnd":1866,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/BindConverter.cs#L1830-L1866","documentation":"Thrown by BindConverter.MakeTypeConverterFormatter when binding a value of type T to output (formatting T to string) and T has no TypeConverter that supports ConvertTo(typeof(string)). Blazor's bind infrastructure falls back to TypeConverter for types without a dedicated formatter; without one, formatting fails at bind time.","triggerScenarios":"Using @bind on a property whose type T has no built-in Blazor formatter and no [TypeConverter] registered that converts to string — typically a custom value type or POCO used directly in an input binding.","commonSituations":"Binding a custom struct/class to an <InputText> or <input>; binding a strongly-typed id (e.g. CustomerId) without a converter; binding enums is fine but custom types are not.","solutions":["Add a TypeConverterAttribute on the type pointing to a TypeConverter subclass that implements ConvertTo for string.","Or bind to a primitive/string property and convert to/from the custom type manually in get/set accessors.","If the type is a record with a single value, expose the underlying primitive for binding."],"exampleFix":"// before: custom type with no converter, bound in markup\npublic record CustomerId(Guid Value);\n<InputText @bind-Value=\"customerId\" />\n\n// after: register a TypeConverter\n[TypeConverter(typeof(CustomerIdConverter))]\npublic record CustomerId(Guid Value);\n\npublic class CustomerIdConverter : TypeConverter\n{\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 CustomerId id ? id.Value.ToString() : null;\n    public override bool CanConvertFrom(ITypeDescriptorContext? c, Type? s) => s == typeof(string);\n    public override object? ConvertFrom(ITypeDescriptorContext? c, CultureInfo? ci, object v)\n        => new CustomerId(Guid.Parse((string)v));\n}","handlingStrategy":"type-guard","validationCode":"var converter = TypeDescriptor.GetConverter(typeof(T));\nif (converter == null || !converter.CanConvertTo(typeof(string))) { /* register a TypeConverter or bind a primitive */ }","typeGuard":"static bool CanFormat<T>() { var c = TypeDescriptor.GetConverter(typeof(T)); return c != null && c.CanConvertTo(typeof(string)); }","tryCatchPattern":null,"preventionTips":["Prefer binding primitives/enums; wrap custom types in a converter when needed.","Add a TypeConverter unit test for any custom bound type.","Keep a checklist: any new bound custom type needs a registered TypeConverter."],"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"}