Skip to content
JSON Tidy
JSON errors

The JSON value could not be converted to System.Int32

Updated by John Marshall

System.Text.Json read the JSON without trouble, but one value can’t be stored in the C# property it maps to. The message names the type it tried to create, and the Path after it says where in the JSON the value is. In The JSON value could not be converted to System.Int32. Path: $.age, the Age property is an int, and the age in the JSON is something an int can’t hold.

Quick fix

  1. Find the value at the Path in your JSON. $ is the top of the document, so $.items[2].price is the price of the third item.
  2. Compare it with the type in the message. The table below lists the usual mismatches.
  3. Change the property’s type to match, or tell the serializer how to read the value, such as JsonNumberHandling.AllowReadingFromString for numbers in quotes.

An example

public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public int Age { get; set; }
}

var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var user = JsonSerializer.Deserialize<User>(
    """{"id": 1, "name": "Maya", "age": "34"}""", options);
System.Text.Json.JsonException: The JSON value could not be converted to System.Int32.
Path: $.age | LineNumber: 0 | BytePositionInLine: 37.

The API sent the age in quotes, so it’s the string "34", and an int property won’t take a string by default.

LineNumber and BytePositionInLine both count from 0, so LineNumber: 0 is the first line. The position points just past the value that failed, and it counts bytes rather than characters, so an é or an emoji earlier on the line moves it further along than you’d expect. The JSON error lookup converts it for you if you paste the message and the JSON together.

If your JSON has camelCase names and the properties are PascalCase, you need PropertyNameCaseInsensitive, as above. Without it, the default options match names exactly, so age never reaches Age. You get no error, just a property left at 0.

What each version means

Each message starts with The JSON value could not be converted to. The type and path after it tell you which case you have:

System.Int32. Path: $.ageA number came in quotes, like "34", or it has a decimal part, or it’s null. An int can’t hold any of those. Fix
System.Boolean. Path: $.activeA boolean came in quotes, like "true". Fix
MyApp.OrderStatus. Path: $.statusAn enum came as text, like "shipped". By default, enums are read as numbers. Fix
System.DateTime. Path: $.createdAtThe date isn’t in ISO 8601 format, like "11/04/2026". Fix
System.String. Path: $.zipThe JSON has a number, like 2108, and the property is a string. Fix
System.Collections.Generic.List`1[MyApp.User]. Path: $You asked for a list, and the JSON is an object. Usually the list is inside it. Fix
MyApp.User. Path: $You asked for one object, and the JSON is a list. Deserialize into List<User>. Fix

Numbers in quotes, with decimals, or null

For numbers sent as strings, let the serializer read them from inside the quotes, either everywhere or for one property:

var options = new JsonSerializerOptions
{
    PropertyNameCaseInsensitive = true,
    NumberHandling = JsonNumberHandling.AllowReadingFromString,
};

// Or for one property
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public int Age { get; set; }

This explains a common puzzle: the same JSON works in an ASP.NET Core controller but fails in a test or console app. ASP.NET Core, and HttpClient’s GetFromJsonAsync, use the web defaults, which already allow numbers in quotes and ignore case in property names. A plain JsonSerializer.Deserialize call uses the strict defaults. Passing new JsonSerializerOptions(JsonSerializerDefaults.Web) gives you the same behavior everywhere.

A value like 34.5 also fails for an int, because the decimal part would be lost. Use decimal or double. A null fails for int, bool, DateTime and other value types, so make the property nullable, like int?, when the API can leave it empty. A number too large for an int, like an ID over 2,147,483,647, needs a long.

Booleans in quotes

There’s no built-in option for "true" in quotes. AllowReadingFromString only covers numbers. A small converter handles both forms:

public class LenientBoolConverter : JsonConverter<bool>
{
    public override bool Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) =>
        reader.TokenType == JsonTokenType.String
            ? bool.Parse(reader.GetString()!)
            : reader.GetBoolean();

    public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) =>
        writer.WriteBooleanValue(value);
}

Add it with options.Converters.Add(new LenientBoolConverter()).

Enums sent as text

By default, System.Text.Json reads enums as numbers, so "status": "shipped" fails while "status": 1 works. Most APIs send the name, so add the string converter:

// Everywhere
options.Converters.Add(new JsonStringEnumConverter());

// In ASP.NET Core
builder.Services.AddControllers().AddJsonOptions(o =>
    o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));

// Or on the enum itself
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum OrderStatus { Pending, Shipped }

Dates

DateTime and DateTimeOffset accept ISO 8601 text, such as "2026-11-04" or "2026-11-04T09:30:00Z". Other formats, like "11/04/2026", fail. If the API uses one of those, read the property as a string and parse it with DateTime.ParseExact, or write a converter for that format. For a date with no time, .NET 7 and later can read it into a DateOnly.

Numbers going into strings

A string property won’t take a JSON number either. This often happens with codes that look numeric, like a ZIP code sent as 2108. Notice that the leading zero of 02108 is already gone. Ask for the API to send it as a string. If that’s not possible, make the property a number type, or write a converter that reads both.

Lists inside an object

When the type in the message is a list and the path is $, the whole response has a different shape from what you asked for. Many APIs return a list inside an object, like {"data": [...], "page": 1}. Deserialize into a class for the outer object, then take the list from it:

public class UserPage
{
    public List<User> Data { get; set; } = new();
    public int Page { get; set; }
}

var users = JsonSerializer.Deserialize<UserPage>(json, options)!.Data;

If the message names your class and the path is $, it’s the other way around: the JSON is a list. Deserialize into List<User>. To see a response’s structure before you write the classes, open it in the JSON viewer. If the error says '<' is an invalid start of a value instead, the server sent HTML, which Unexpected token '<' covers.

The same error elsewhere

C#, System.Text.JsonThe JSON value could not be converted to System.Int32. Path: $.age | LineNumber: 0 | BytePositionInLine: 37.
C#, Newtonsoft.Json (lists)Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyApp.User]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
C#, Newtonsoft.Json (null)Error converting value {null} to type 'System.Int32'. Path 'age'
Java, JacksonCannot deserialize value of type `int` from String "abc": not a valid `int` value
Gojson: cannot unmarshal string into Go struct field User.age of type int