microsoft/garnet · error · RedisSerializationException

Unable to deserialize RedisOptions object. Line

Error message

Unable to deserialize RedisOptions object. Line {lineCount} not in expected format (keyword argument1 argument2 ... argumentN).

What it means

Thrown by RedisConfigSerializer.Deserialize when a non-comment, non-blank line in a Redis-format config file has no space character. Every directive in a Redis .conf file must follow 'keyword argument1 argument2 ... argumentN'. A line with only a keyword and no arguments (no space) is rejected because the parser cannot extract a value portion.

Solutions

  1. Inspect the line number reported in the error message and add the missing argument(s) after the keyword.
  2. If the directive genuinely takes no arguments, add a dummy value or remove the line if it's not needed.
  3. Validate the config file format: each non-comment line must contain at least one space separating the keyword from its value.

Example fix

// before (line 3 of config file):
//   save

// after:
//   save 3600 1 300 100 60 10000
Defensive patterns

Strategy: validation

Validate before calling

foreach (var (line, num) in File.ReadLines(configPath).Select((l, i) => (l, i + 1)))
{
    var trimmed = line.TrimStart();
    if (string.IsNullOrWhiteSpace(trimmed) || trimmed.StartsWith('#')) continue;
    if (!line.Contains(' '))
        throw new FormatException($"Line {num}: directive '{trimmed}' has no arguments (expected 'keyword arg1 ...')");
}

Type guard

static bool IsValidConfigLine(string line)
{
    var t = line.TrimStart();
    if (string.IsNullOrWhiteSpace(t) || t.StartsWith('#')) return true;
    return line.Contains(' ');
}

Try / catch

try
{
    var redisOpts = RedisConfigSerializer.Deserialize(reader, logger);
}
catch (RedisSerializationException ex) when (ex.Message.Contains("not in expected format"))
{
    logger.LogError("Redis config file has a malformed line: {msg}", ex.Message);
    throw;
}

Prevention

When it happens

Trigger: Loading a redis.conf-style file where a directive line is a single bare token with no space separator (e.g., a line containing only 'save' with no arguments). The code at line 75-76 uses IndexOf(' '); -1 means no space was found.

Common situations: Hand-edited Redis config files where a user accidentally deleted the argument portion; copy-paste from documentation that truncated the example; config generation tool that emitted keyword-only lines for boolean directives.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/df2b4def3a461967. Report an issue: GitHub.

Appendix: source

Thrown at libs/host/Configuration/Redis/RedisConfigSerializer.cs:77

        /// <exception cref="RedisSerializationException"></exception>
        public static RedisOptions Deserialize(StreamReader reader, ILogger logger)
        {
            var options = new RedisOptions();

            int lineCount = 0;
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                lineCount++;

                // Ignore whitespaces and comments
                if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith('#'))
                    continue;

                // Expected line format: keyword argument1 argument2 ... argumentN
                var sepIdx = line.IndexOf(' ');
                if (sepIdx == -1)
                    throw new RedisSerializationException(
                        $"Unable to deserialize {nameof(RedisOptions)} object. Line {lineCount} not in expected format (keyword argument1 argument2 ... argumentN).");

                // Ignore key when no matching property found 
                var key = line.Substring(0, sepIdx);
                if (!KeyToProperty.Value.ContainsKey(key))
                {
                    logger?.LogWarning("Redis configuration option not supported: {key}.", key);
                    continue;
                }

                var value = line.Substring(sepIdx + 1);

                // Get matching property & the underlying option type (T in Option<T>)
                var prop = KeyToProperty.Value[key];
                var optType = prop.PropertyType.GenericTypeArguments.First();

                // Try to deserialize the value
                if (!TryChangeType(value, typeof(string), optType, out var newVal))

View on GitHub (pinned to 951b0fc683)