reactiveui/refit · error · JsonException

Unable to convert '{value}' to {typeof(TEnum)}.

Error message

Unable to convert '{value}' to {typeof(TEnum)}.

What it means

Thrown by CamelCaseStringEnumConverter when the JSON string token's value does not match any enum field name (case-sensitively or insensitively) and is not numeric. The converter tried both the exact-name and ignore-case maps and found nothing, so it cannot produce a valid enum value and raises a JsonException naming the bad value.

Source

Thrown at src/Refit/CamelCaseStringEnumConverter.cs:221

            if (reader.TokenType is JsonTokenType.String or JsonTokenType.PropertyName)
            {
                var value = reader.GetString();
                if (value is null || string.IsNullOrWhiteSpace(value))
                {
                    throw new JsonException($"Cannot convert an empty value to {typeof(TEnum)}.");
                }

                if (_namesToValues.TryGetValue(value!, out var namedValue))
                {
                    return namedValue;
                }

                if (_namesToValuesIgnoreCase.TryGetValue(value!, out var namedValueIgnoreCase))
                {
                    return namedValueIgnoreCase;
                }

                throw new JsonException($"Unable to convert '{value}' to {typeof(TEnum)}.");
            }

            if (reader.TokenType == JsonTokenType.Number)
            {
                return EnumHelpers.Info<TEnum>.ReadJsonNumericValue(ref reader);
            }

            throw new JsonException($"Unexpected token {reader.TokenType} when parsing {typeof(TEnum)}.");
        }
    }

    /// <summary>A strongly-typed JSON converter for nullable enums that maps values to and from camelCase names.</summary>
    /// <typeparam name="TEnum">The underlying enum type.</typeparam>
    internal sealed class NullableEnumConverter<
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TEnum> : JsonConverter<TEnum?>
        where TEnum : struct, Enum
    {
        /// <summary>The underlying non-nullable enum converter that performs the name/value mapping.</summary>

View on GitHub (pinned to b455f65ecc)

Solutions

  1. Align the JSON value with a valid enum field name, respecting the camelCase naming the converter expects (or configure the converter's naming policy).
  2. If the client may receive unknown values, deserialize into the enum backed by JsonStringEnumConverter with a fallback, or use a string property plus manual parsing with Enum.TryParse.
  3. Resolve version skew by adding the missing enum member or normalizing the source value.

Example fix

// before — enum mismatch / typo
public enum Status { Active, Inactive }
// JSON: { "status": "actv" }  -> throws

// after — correct camelCase name (or add [JsonStringEnumName])
// JSON: { "status": "active" }
// or parse defensively:
var raw = JsonDocument.Parse(json).RootElement.GetProperty("status").GetString();
if (Enum.TryParse<Status>(raw, ignoreCase: true, out var s)) { /* use s */ }
Defensive patterns

Strategy: validation

Validate before calling

// Validate a value maps to the enum before deserializing the whole payload.
static bool IsKnownEnumName<TEnum>(string? name) where TEnum : struct, Enum =>
    name is not null && Enum.TryParse<TEnum>(name, ignoreCase: true, out _);

// or: keep a whitelist of valid camelCase names
static readonly HashSet<string> Valid = new(Enum.GetNames<Status>().Select(CamelCase));

Type guard

// Loose-parse guard using the underlying enum.
static bool TryReadEnum<TEnum>(string? raw, out TEnum value) where TEnum : struct, Enum =>
    Enum.TryParse(raw, ignoreCase: true, out value);

Try / catch

try { return JsonSerializer.Deserialize<Payload>(json); }
catch (JsonException ex) when (ex.Message.Contains("Unable to convert"))
{ /* unknown enum value — log and use a default/Unknown member */ }

Prevention

When it happens

Trigger: Deserializing an enum from a JSON string whose name doesn't exist, e.g. enum Status { Active, Inactive } and JSON `{ "status": "Actv" }` (typo) or `{ "status": "PENDING" }` (wrong casing/variant). The camelCase mapping also matters: names are matched against the camelCased enum field names.

Common situations: Server/client enum naming drift (e.g. server sends snake_case or SCREAMING_CASE while converter expects camelCase); typos; a new enum value the client doesn't have yet (version skew); sending a display label instead of the code name.

Related errors


AI-assisted analysis of reactiveui/refit@b455f65ecc (2026-08-13). Data as JSON: /api/errors/33888ecd8d702514. Report an issue: GitHub.