microsoft/garnet · error · GarnetException

v3 RespCommand '{V3Order[i]}' (legacy AOF value {i + 1}) no

Error message

v3 RespCommand '{V3Order[i]}' (legacy AOF value {i + 1}) no longer maps to a current RespCommand. Renaming or removing a persisted (write) command breaks v3 AOF compatibility; add an explicit mapping.

What it means

Thrown at static initialization time (BuildMap / EnsureInitialized) when a name in the LegacyRespCommand.V3Order table does not parse to a current RespCommand enum value AND is not listed in RemovedNonPersistedV3Commands. This is a fail-fast guard: renaming or removing a persisted (write) command breaks v3 AOF replay compatibility, so the build refuses to start rather than silently mis-replaying old AOF files.

Source

Thrown at libs/server/AOF/LegacyRespCommand.cs:101

        {
            // Size covers NONE (0) plus all v3 built-in members (values 1..V3Order.Length).
            var map = new RespCommand[V3Order.Length + 1];
            map[0] = RespCommand.NONE;
            for (var i = 0; i < V3Order.Length; i++)
            {
                if (!Enum.TryParse<RespCommand>(V3Order[i], out var current))
                {
                    // A read-only/admin command that has since been removed from the enum (e.g. folded into DEBUG
                    // as a subcommand). It was never persisted in the AOF (only write commands are), so its v3
                    // numeric slot is never read back; map it to NONE. Removing a persisted (write) command is NOT
                    // permitted here and still fails fast.
                    if (RemovedNonPersistedV3Commands.Contains(V3Order[i]))
                    {
                        map[i + 1] = RespCommand.NONE;
                        continue;
                    }

                    throw new GarnetException(
                        $"v3 RespCommand '{V3Order[i]}' (legacy AOF value {i + 1}) no longer maps to a current RespCommand. " +
                        "Renaming or removing a persisted (write) command breaks v3 AOF compatibility; add an explicit mapping.");
                }

                map[i + 1] = current;
            }

            return map;
        }

        /// <summary>
        /// Translate a RespCommand value read from a v3 (pre-v4) AOF entry to the current numbering.
        /// Custom raw-string command ids (anchored to <see cref="RespCommand.INVALID"/>) and any value
        /// outside the built-in range are unchanged.
        /// </summary>
        internal static RespCommand FromV3(RespCommand v3Command)
        {
            var value = (ushort)v3Command;

View on GitHub (pinned to 951b0fc683)

Solutions

  1. If the removed command was a write command persisted in the AOF, add an explicit mapping in BuildMap (or keep the old enum value as an alias) rather than deleting it.
  2. If the command was read-only/admin and never persisted, add its old name to the RemovedNonPersistedV3Commands HashSet (which maps it to RespCommand.NONE).
  3. Run LegacyRespCommand.EnsureInitialized() in unit tests to catch the regression before deployment.
  4. Review the PR diff against V3Order to ensure no persisted write command was renamed or removed without a mapping.

Example fix

// before: renamed READCMD to GETREAD in RespCommand, breaking v3 compat

// after: add the old name to the non-persisted set (if read-only)
private static readonly HashSet<string> RemovedNonPersistedV3Commands = new(StringComparer.Ordinal) { "PURGEBP", "READCMD" };
// OR if it was a persisted write command, keep an explicit alias mapping in BuildMap.
Defensive patterns

Strategy: validation

Validate before calling

// In the test suite, force early initialization so the regression fails at test time, not in prod
Garnet.server.LegacyRespCommand.EnsureInitialized(); // call in a startup/unit test

// During development, after any RespCommand enum change, verify all V3Order entries resolve:
// - read-only/admin removals -> add to RemovedNonPersistedV3Commands
// - persisted write command rename/removal -> add explicit mapping in BuildMap

Prevention

When it happens

Trigger: A developer renames or deletes a write command in the RespCommand enum without either (a) adding it to RemovedNonPersistedV3Commands (only valid if it was never persisted) or (b) adding an explicit v3->current mapping in BuildMap. The exception fires at process startup when V3ToCurrent is first accessed.

Common situations: Refactoring RespCommand during Garnet development; merging a PR that renames a command without updating the legacy compat table; this is a build-time/source-level regression, not an operator error.

Related errors


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