{"record":{"id":"398b52e7b894f52e","repo":"microsoft/garnet","slug":"specified-transformer-for-redisoptions-property-do","errorCode":null,"errorMessage":"Specified transformer for RedisOptions property does not match given types (Source: {typeof(TIn)}, Destination: {typeof(TOut)}).","messagePattern":"Specified transformer for RedisOptions property does not match given types \\(Source: (.+?), Destination: (.+?)\\)\\.","errorType":"exception","errorClass":"RedisSerializationException","httpStatus":null,"severity":"critical","filePath":"libs/host/Configuration/Redis/RedisConfigSerializer.cs","lineNumber":223,"sourceCode":"\n                // Get destination property type\n                var dstPropType = dstProp.PropertyType;\n\n                // Try to convert source option to destination option\n                object dstValue = null;\n\n                // If custom transformer specified in RedisOptionAttribute, try to invoke transformer\n                if (redisOptionAttr.GarnetCustomTransformer != null)\n                {\n                    var parameters = new[] { srcOptValue, null, null };\n                    var result = (bool?)typeof(RedisConfigSerializer).GetMethod(nameof(TryApplyTransform), BindingFlags.NonPublic | BindingFlags.Static)\n                        ?.MakeGenericMethod(srcOptValueType, dstPropType, redisOptionAttr.GarnetCustomTransformer)\n                        .Invoke(null, parameters);\n                    dstValue = parameters[1];\n\n                    if (!result.HasValue || !result.Value)\n                    {\n                        throw new RedisSerializationException((string)parameters[2]);\n                    }\n                }\n                // Try to convert to destination type\n                else if (srcOptValueType != dstPropType)\n                {\n                    if (!TryChangeType(srcOptValue, srcOptValueType, dstPropType, out dstValue))\n                    {\n                        throw new RedisSerializationException(\n                            $\"Unable to convert property {prop.Name} in {typeof(RedisOptions)} to property {redisOptionAttr.GarnetOptionName} in {typeof(Options)}.\");\n                    }\n                }\n                // Source and destination types are the same with no transformer specified\n                else\n                {\n                    dstValue = srcOptValue;\n                }\n\n                // Set the destination property with the converted value","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/host/Configuration/Redis/RedisConfigSerializer.cs#L205-L241","documentation":"Thrown indirectly via TryApplyTransform when a custom transformer type specified in RedisOptionAttribute.GarnetCustomTransformer does not implement IGarnetCustomTransformer<TIn, TOut> with the exact generic argument types matching the source and destination properties. This is a compile-time-to-runtime type contract violation in the framework's mapping layer.","triggerScenarios":"A RedisOptionAttribute has a GarnetCustomTransformer set to a type whose IGarnetCustomTransformer<TIn,TOut> generic arguments don't match the RedisOptions property type (TIn) or the Options property type (TOut). The check at RedisConfigSerializer.cs:264 uses IsAssignableFrom and fails.","commonSituations":"Changing a property type on either RedisOptions or Options without updating the transformer's generic arguments; using a transformer designed for a different property pair; framework development error.","solutions":["Check the error message for the expected Source (TIn) and Destination (TOut) types.","Update the custom transformer class to implement IGarnetCustomTransformer<TIn, TOut> with the correct types.","If the property types changed, update both the transformer and the RedisOptionAttribute."],"exampleFix":"// before: transformer implements wrong generic args\n//   class MyTransformer : IGarnetCustomTransformer<int, string> { ... }\n//   // but source is actually 'long'\n\n// after:\n//   class MyTransformer : IGarnetCustomTransformer<long, string> { ... }","handlingStrategy":"validation","validationCode":"// Verify a transformer type matches the expected interface before assignment\nvar transformerType = typeof(MyTransformer);\nvar expectedInterface = typeof(IGarnetCustomTransformer<,>).MakeGenericType(srcType, dstType);\nif (!expectedInterface.IsAssignableFrom(transformerType))\n    throw new InvalidOperationException(\n        $\"Transformer {transformerType.Name} must implement {expectedInterface.Name}\");","typeGuard":"static bool TransformerMatchesTypes(Type transformerType, Type srcType, Type dstType)\n{\n    var expectedInterface = typeof(IGarnetCustomTransformer<,>).MakeGenericType(srcType, dstType);\n    return expectedInterface.IsAssignableFrom(transformerType);\n}","tryCatchPattern":null,"preventionTips":["Ensure custom transformers implement IGarnetCustomTransformer<TIn, TOut> with types matching both properties.","Update transformer generic arguments when property types change.","Add unit tests that verify transformer type compatibility at build time."],"tags":["redis-config","reflection","type-mismatch","framework-bug"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}