{"record":{"id":"9a05b57a187461ec","repo":"microsoft/garnet","slug":"unable-to-deserialize-redisoptions-object-unable-9a05b5","errorCode":null,"errorMessage":"Unable to deserialize RedisOptions object. Unable to convert object of type {typeof(string)} to object of type {optType}. (Line: {lineCount}; Key: {key}; Property: {prop.Name}).","messagePattern":"Unable to deserialize RedisOptions object\\. Unable to convert object of type (.+?) to object of type (.+?)\\. \\(Line: (.+?); Key: (.+?); Property: (.+?)\\)\\.","errorType":"exception","errorClass":"RedisSerializationException","httpStatus":null,"severity":"error","filePath":"libs/host/Configuration/Redis/RedisConfigSerializer.cs","lineNumber":120,"sourceCode":"\n                        // Instantiate a new array\n                        var elemType = optType.GetElementType();\n                        newVal = Array.CreateInstance(elemType, values.Length);\n\n                        // Try deserializing and setting array elements\n                        for (var i = 0; i < values.Length; i++)\n                        {\n                            if (!TryChangeType(values[i], typeof(string), elemType, out var elem))\n                                throw new RedisSerializationException(\n                                    $\"Unable to deserialize {nameof(RedisOptions)} object. Unable to convert object of type {typeof(string)} to object of type {elemType}. (Line: {lineCount}; Key: {key}; Property: {prop.Name}).\");\n\n\n                            ((Array)newVal).SetValue(elem, i);\n                        }\n                    }\n                    else\n                    {\n                        throw new RedisSerializationException(\n                            $\"Unable to deserialize {nameof(RedisOptions)} object. Unable to convert object of type {typeof(string)} to object of type {optType}. (Line: {lineCount}; Key: {key}; Property: {prop.Name}).\");\n                    }\n                }\n\n                // Create a new Option<T> object\n                var newOpt = Activator.CreateInstance(prop.PropertyType);\n\n                // Set the underlying option value\n                var valueProp = prop.PropertyType.GetProperty(nameof(Option<object>.Value));\n                valueProp.SetValue(newOpt, newVal);\n\n                // Set the options property to the new option object\n                prop.SetValue(options, newOpt);\n\n                // Append usage warning, if defined\n                var redisOptionAttr = (RedisOptionAttribute)prop.GetCustomAttributes(typeof(RedisOptionAttribute), false).First();\n                if (!string.IsNullOrEmpty(redisOptionAttr.UsageWarning))\n                {","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/host/Configuration/Redis/RedisConfigSerializer.cs#L102-L138","documentation":"Thrown by RedisConfigSerializer.Deserialize when a non-array option value cannot be converted from string to the option's underlying type. This is the fallback after TryChangeType fails for scalar options — e.g., providing 'abc' for an integer property. The parser tried TypeDescriptor converters and Convert.ChangeType, all of which failed.","triggerScenarios":"A config directive whose value is a single scalar that doesn't match the property type. For example, 'port abc' where Port expects an int, or 'timeout true' where the property expects a numeric value.","commonSituations":"Typographical errors in config files; unit mismatches (e.g., providing '10ms' where a raw integer is expected); locale-related number format issues; using a Redis directive name that maps to a different type than expected.","solutions":["Check the property name and key in the error message to identify the directive.","Provide a value that matches the expected type (e.g., integer for Port, valid boolean or numeric for timeout).","Refer to the GarnetServerOptions property type for the correct value format."],"exampleFix":"// before:\n//   port abc\n\n// after:\n//   port 6379","handlingStrategy":"validation","validationCode":"var propType = typeof(RedisOptions).GetProperty(key)?.PropertyType;\nif (propType != null)\n{\n    var optType = propType.GenericTypeArguments.First();\n    var converter = TypeDescriptor.GetConverter(optType);\n    if (!converter.IsValid(value))\n        throw new FormatException($\"Value '{value}' is not valid for type {optType.Name} (key: {key})\");\n}","typeGuard":"static bool CanConvertScalar(string value, Type targetType)\n{\n    var converter = TypeDescriptor.GetConverter(targetType);\n    return converter.IsValid(value);\n}","tryCatchPattern":"try\n{\n    var redisOpts = RedisConfigSerializer.Deserialize(reader, logger);\n}\ncatch (RedisSerializationException ex) when (ex.Message.Contains(\"Unable to convert object of type\"))\n{\n    logger.LogError(\"Redis config value type mismatch: {msg}\", ex.Message);\n    throw;\n}","preventionTips":["Match config values to the expected property types (int, string, bool, etc.).","Pre-validate values using TypeDescriptor before loading config.","Document the expected type for each directive in config templates."],"tags":["redis-config","deserialization","type-conversion","configuration"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}