BrighterCommand / BrighterCommand/Brighter

JsonSerialisationOptions.Options omits IncludeFields, silently dropping public fields to default

Open
#4,237 0 comments 0 reactions 0 assignees View on GitHub
Bug V11
Dominant language
C#
Stars
2.5k
Forks
296
Avg merge
1d 11h
Merged PRs (30d)
21

Description

### Describe the bug

`JsonSerialisationOptions.Options` (`src/Paramore.Brighter/JsonConverters/JsonSerialisationOptions.cs:25-31`) never sets `IncludeFields`, which defaults to `false` in `System.Text.Json`. Any **public field** on a serialised type is therefore silently dropped and arrives as `default`. No exception is thrown.

This surfaced while confirming the root cause of #4152. That issue's stated cause (positional `readonly record struct` failing to bind through its primary constructor) was **refuted empirically** — positional record structs round-trip correctly. But while testing, this defect reproduced end-to-end through the full Azure scheduler envelope and produces the *exact* symptom #4152 reports: a member silently arriving as `default`.

### To Reproduce

Round-trip any type with a public field through `JsonSerialisationOptions.Options`:

```csharp
public record MyRequest : Command
{
public int PlainField; // silently dropped -> 0
}
```

Verified end-to-end through the scheduler path (serialize at `AzureServiceBusScheduler.cs:83` -> envelope -> `FireAzureScheduler.MapToRequest` -> `FireSchedulerRequestHandler.cs:62`):

```
FINAL: Type=Delete Value='abc-123' PlainField=0
ActionId survived end-to-end: True
PlainField survived end-to-end: False
```

Reproduced identically on net8.0 / net9.0 / net10.0 (STJ 8.0.0.0 / 9.0.0.0 / 10.0.0.0).

### Expected behaviour

Either public fields round-trip, or the contract ("scheduled requests must use properties, not fields") is documented and ideally enforced loudly rather than failing silently.

### Why V11

The obvious fix is `IncludeFields = true`, but `JsonSerialisationOptions.Options` is a **shared static** used at every Brighter serialisation site — message bodies, outbox/inbox persistence, scheduler envelopes. Turning it on changes the wire and at-rest format for any user type carrying public fields: newly-emitted JSON gains members it did not have before, which existing consumers and stored rows were never written to expect.

That is a significant behavioural change and does not belong in a patch release. Flagging for V11 so it can be taken with a proper compatibility review, and so we can decide whether the right answer is the global flag or a narrower, documented contract for scheduled requests.

### Related

- #4152 — where this was found. Its own stated cause is refuted; this is a candidate for the behaviour that issue actually observed.

Contributor guide

Open the contributing guide

Research direction

Start with src/Paramore.Brighter/JsonConverters/JsonSerialisationOptions.cs:25-31 and inspect the shared Options usage at AzureServiceBusScheduler.cs:83 and FireSchedulerRequestHandler.cs:62. Trace the envelope through FireAzureScheduler.MapToRequest, then determine whether public fields should round-trip or whether the scheduled-request contract must be documented and enforced with compatibility in mind.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.