microsoft/garnet · critical · RedisSerializationException
Specified transformer for RedisOptions property does not mat
Error message
Specified transformer for RedisOptions property does not match given types (Source: {typeof(TIn)}, Destination: {typeof(TOut)}). What it means
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.
Source
Thrown at libs/host/Configuration/Redis/RedisConfigSerializer.cs:223
// Get destination property type
var dstPropType = dstProp.PropertyType;
// Try to convert source option to destination option
object dstValue = null;
// If custom transformer specified in RedisOptionAttribute, try to invoke transformer
if (redisOptionAttr.GarnetCustomTransformer != null)
{
var parameters = new[] { srcOptValue, null, null };
var result = (bool?)typeof(RedisConfigSerializer).GetMethod(nameof(TryApplyTransform), BindingFlags.NonPublic | BindingFlags.Static)
?.MakeGenericMethod(srcOptValueType, dstPropType, redisOptionAttr.GarnetCustomTransformer)
.Invoke(null, parameters);
dstValue = parameters[1];
if (!result.HasValue || !result.Value)
{
throw new RedisSerializationException((string)parameters[2]);
}
}
// Try to convert to destination type
else if (srcOptValueType != dstPropType)
{
if (!TryChangeType(srcOptValue, srcOptValueType, dstPropType, out dstValue))
{
throw new RedisSerializationException(
$"Unable to convert property {prop.Name} in {typeof(RedisOptions)} to property {redisOptionAttr.GarnetOptionName} in {typeof(Options)}.");
}
}
// Source and destination types are the same with no transformer specified
else
{
dstValue = srcOptValue;
}
// Set the destination property with the converted valueView on GitHub (pinned to 951b0fc683)
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.
Example fix
// before: transformer implements wrong generic args
// class MyTransformer : IGarnetCustomTransformer<int, string> { ... }
// // but source is actually 'long'
// after:
// class MyTransformer : IGarnetCustomTransformer<long, string> { ... } Defensive patterns
Strategy: validation
Validate before calling
// Verify a transformer type matches the expected interface before assignment
var transformerType = typeof(MyTransformer);
var expectedInterface = typeof(IGarnetCustomTransformer<,>).MakeGenericType(srcType, dstType);
if (!expectedInterface.IsAssignableFrom(transformerType))
throw new InvalidOperationException(
$"Transformer {transformerType.Name} must implement {expectedInterface.Name}"); Type guard
static bool TransformerMatchesTypes(Type transformerType, Type srcType, Type dstType)
{
var expectedInterface = typeof(IGarnetCustomTransformer<,>).MakeGenericType(srcType, dstType);
return expectedInterface.IsAssignableFrom(transformerType);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unable to find property in {typeof(Options)} named {redisOpt
- Unable to convert property {prop.Name} in {typeof(RedisOptio
- Unable to deserialize RedisOptions object. Line {lineCount}
- Unable to deserialize RedisOptions object. Unable to convert
- Unable to deserialize RedisOptions object. Unable to convert
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/398b52e7b894f52e.
Report an issue: GitHub.