Updated October 2026 for .NET 10 (LTS) and System.Text.Json 10.

This error means the JSON has a value whose type does not match your C# property. Most often a number or true/false arrives where your class expects a string. System.Text.Json does not convert between types silently, so it stops. Below are the common variants, why they happen, and three ways to fix them.

The error

System.Text.Json.JsonException: The JSON value could not be converted to System.String. Path: $.EmployeeId | LineNumber: 3 | BytePositionInLine: 22.

The same message appears for other types, such as System.DateTime, System.Boolean or System.Collections.Generic.List`1. The Path part tells you exactly which property failed.

Why it happens

Here the JSON sends numbers and a boolean:

{
  "FirstName": "Ann",
  "LastName": "Lee",
  "EmployeeId": 1042,
  "IsPermanent": true
}

But the class declares them as strings:

public class Customer
{
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
    public string EmployeeId { get; set; } = "";   // JSON has a number
    public string IsPermanent { get; set; } = "";  // JSON has true/false
}

var customer = JsonSerializer.Deserialize<Customer>(json); // throws

Newtonsoft.Json quietly turns 1042 into "1042", so this often shows up after switching to System.Text.Json. Other common causes for other types:

  • System.DateTime: the date is not in ISO 8601 format, for example "21/10/2026" instead of "2026-10-21".
  • List or array: the JSON has a single object { } where the class expects an array [ ].
  • Nested object: the JSON has a string where the class expects an object.

Fix 1: Match your C# types to the JSON (best fix)

If EmployeeId is always a number, make it a number in C#:

public class Customer
{
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
    public int EmployeeId { get; set; }
    public bool IsPermanent { get; set; }
}

The quickest way to get matching types is to paste a sample of the JSON into the free C# Class Converter. It generates classes with the right types for every field.

Fix 2: Accept numbers and booleans in string properties

If the sender is inconsistent (sometimes "1042", sometimes 1042) and the property must stay a string, add a small converter:

using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

public sealed class FlexibleStringConverter : JsonConverter<string>
{
    public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
        reader.TokenType switch
        {
            JsonTokenType.String => reader.GetString(),
            JsonTokenType.Number => Encoding.UTF8.GetString(reader.ValueSpan), // keeps the exact digits
            JsonTokenType.True => "true",
            JsonTokenType.False => "false",
            JsonTokenType.Null => null,
            _ => throw new JsonException($"Cannot read {reader.TokenType} as a string.")
        };

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

Use it for one property:

[JsonConverter(typeof(FlexibleStringConverter))]
public string EmployeeId { get; set; } = "";

Or for every string property:

var options = new JsonSerializerOptions();
options.Converters.Add(new FlexibleStringConverter());

var customer = JsonSerializer.Deserialize<Customer>(json, options);

In ASP.NET Core, register it once in Program.cs so request bodies use it too:

// Controllers
builder.Services.AddControllers()
    .AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new FlexibleStringConverter()));

// Minimal APIs
builder.Services.ConfigureHttpJsonOptions(o =>
    o.SerializerOptions.Converters.Add(new FlexibleStringConverter()));

More on writing converters: Custom converters with System.Text.Json.

Fix 3: Use Newtonsoft.Json for that API

If you are migrating a large API that relied on Newtonsoft’s loose conversions, you can keep Newtonsoft for now and move over gradually:

// dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
builder.Services.AddControllers().AddNewtonsoftJson();

See Add Newtonsoft.Json support in ASP.NET Core. For new code, prefer Fix 1.

FAQ

Is there a built-in option for numbers into strings?

No. JsonNumberHandling.AllowReadingFromString works the other way round: it lets a quoted number like "42" fill an int property. To read a number into a string, use a converter as in Fix 2.

How do I find which property failed?

Read the Path in the exception message, for example $.Address.ZipCode. It points to the exact JSON property, including nested ones.

Related

Sources: Microsoft Learn: Write custom converters · Microsoft Learn: Migrate from Newtonsoft.Json