OrchardCMS/OrchardCore · error · InvalidCastException

Cannot convert to SByte

Error message

Cannot convert {value} to SByte

What it means

JsonDynamicValue wraps a JSON value and exposes explicit conversion operators for primitive types. The explicit operator sbyte casts _jsonValue.GetValue<sbyte>(); if the underlying JSON value is absent, null, or not representable as an SByte, the operator returns null and throws InvalidCastException with 'Cannot convert {value} to SByte'. It signals that the requested numeric conversion is not possible for the wrapped JSON token.

Solutions

  1. Cast to sbyte? (explicit operator sbyte?) instead of sbyte, which returns null instead of throwing, then handle the null case.
  2. Validate the JSON value's numeric type and range (-128..127) with TryGetValue before casting.
  3. Cast to a wider type (short/int) first if the data may exceed the sbyte range, and range-check explicitly.
  4. Wrap the cast in try/catch for InvalidCastException if the value is genuinely optional.

Example fix

// before
sbyte level = (sbyte)jsonValue; // throws when jsonValue is 200 or "5"

// after
sbyte? level = (sbyte?)jsonValue;
if (level is null) { /* handle missing/invalid value */ }
Defensive patterns

Strategy: type-guard

Validate before calling

if (jsonValue is null || jsonValue.Type != JTokenType.Integer)
    throw new InvalidOperationException("Expected an integer JSON value for SByte cast.");
if (jsonValue.Value<long>() is < sbyte.MinValue or > sbyte.MaxValue)
    throw new InvalidOperationException($"Value {jsonValue} is outside SByte range.");

Type guard

static bool IsConvertibleToSByte(JsonDynamicValue v) =>
    v?._jsonValue is { Type: JTokenType.Integer } t && t.Value<long>() is >= sbyte.MinValue and <= sbyte.MaxValue;

Try / catch

try
{
    sbyte n = (sbyte)jsonValue;
}
catch (InvalidCastException ex)
{
    // log and fall back to a default or skip the record
    sbyte n = default;
}

Prevention

When it happens

Trigger: Casting a JsonDynamicValue instance to sbyte with (sbyte)jsonDynamicValue when the wrapped JSON token is null, not a number, or a number outside the SByte range (-128..127), causing GetValue<sbyte>() to yield null.

Common situations: Reading small flags/ratings from JSON where the value is a string or exceeds 127, casting on a JsonDynamicValue built from a null token, or data contract changes shrinking the expected type to sbyte while producers still emit larger values.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/3e6486888879e1c5. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore.Abstractions/Json/Dynamic/JsonDynamicValue.cs:395

    {
        return value?._jsonValue?.GetValue<int?>();
    }

    public static explicit operator long(JsonDynamicValue value)
    {
        return value?._jsonValue?.GetValue<long>()
            ?? throw new InvalidCastException($"Cannot convert {value} to Int64");
    }

    public static explicit operator long?(JsonDynamicValue value)
    {
        return value?._jsonValue?.GetValue<long?>();
    }

    public static explicit operator sbyte(JsonDynamicValue value)
    {
        return value?._jsonValue?.GetValue<sbyte>()
            ?? throw new InvalidCastException($"Cannot convert {value} to SByte");
    }

    public static explicit operator sbyte?(JsonDynamicValue value)
    {
        return value?._jsonValue?.GetValue<sbyte?>();
    }

    public static explicit operator float(JsonDynamicValue value)
    {
        return value?._jsonValue?.GetValue<float>()
            ?? throw new InvalidCastException($"Cannot convert {value} to Float");
    }

    public static explicit operator float?(JsonDynamicValue value)
    {
        return value?._jsonValue?.GetValue<float?>();
    }

View on GitHub (pinned to 4306c0717f)