InvalidOperationException: This JsonSerializerOptions instance is read-only or has
already been used in serialization or deserialization is thrown when code changes a
JsonSerializerOptions object after that object was passed to
JsonSerializer. Finish configuring the options (converters, naming policy and
the rest) before the first Serialize or Deserialize call that uses
them.
In the reproduction below, a JsonStringEnumConverter was added to an options
object that had already serialized one record. Building a second options object with the
converter added before its first use made the same serialization work.
The error
{"Name":"David Chen","Status":0}
Unhandled exception. System.InvalidOperationException: This JsonSerializerOptions instance is read-only or has already been used in serialization or deserialization.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerOptionsReadOnly(JsonSerializerContext context)
at System.Text.Json.JsonSerializerOptions.VerifyMutable()
at System.Text.Json.Serialization.ConfigurationList`1.Add(TItem item)
at Program.<Main>$(String[] args) in C:\...\ro\Program.cs:line 15
The first line is the successful serialization with the untouched options; the exception
comes from the next statement. Note what is missing: no path, no line number and no JSON
position, because nothing was being read or written when it was thrown. The last stack
frame, Program.cs:line 15, is the line to look at.
Why it happens
The code that failed was this, with nothing between the two parts but an
if block that did not run:
var options = new JsonSerializerOptions();
Console.WriteLine(JsonSerializer.Serialize(new Patient("David Chen", Status.Active), options));
...
options.Converters.Add(new JsonStringEnumConverter());
The stack trace shows the path: ConfigurationList`1.Add (the
Converters list) called JsonSerializerOptions.VerifyMutable(),
which threw. The first Serialize call had locked the options, and from then on
adding a converter is refused. The message names both ways an options object gets into
this state: it is read-only, or it has already been used.
The fix
var fixedOptions = new JsonSerializerOptions();
fixedOptions.Converters.Add(new JsonStringEnumConverter());
Console.WriteLine(JsonSerializer.Serialize(new Patient("David Chen", Status.Active), fixedOptions));
Running dotnet run -- fix printed the old options' result and then the new one:
{"Name":"David Chen","Status":0}
{"Name":"David Chen","Status":"Active"}
The converter is added on the line after new JsonSerializerOptions(), before
the options reach the serializer, and the enum now comes out as "Active"
instead of 0. If two parts of a program need different settings, give each
its own options object, configured completely before its first use, and keep reusing that
instance afterwards.
How it was reproduced
A console project from dotnet new console on .NET SDK 10.0.403
(net10.0, no extra packages; System.Text.Json is part of the framework), a
Patient record with a Status enum property, one
JsonSerializer.Serialize call with a fresh options object and then
options.Converters.Add(new JsonStringEnumConverter()) on the same object. Run
with dotnet run for the error and dotnet run -- fix for the
corrected order.
Frequently asked
- Why can I not add a converter to JsonSerializerOptions?
- The options object was already passed to JsonSerializer.Serialize or Deserialize. After that first use, adding to Converters calls VerifyMutable, which throws InvalidOperationException. Add converters before the first call.
- How do I fix JsonSerializerOptions instance is read-only?
- Configure the options completely right after creating them, then use them. If you need different settings later, create a new JsonSerializerOptions object with those settings instead of changing the one already in use.
- Does the exception mean the first serialization failed?
- No. In the reproduction the first Serialize call printed its JSON normally. The exception came from the next statement, the Converters.Add call on the options that call had used.
More decoded errors in the Fixes category.