You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#4054 / #4205 fixed the ASB AzureServiceBusMessagePublisher, where a SessionId bag key was silently dropped after an Outbox round-trip. Root cause: the Outbox serializes the header Bag with JsonSerialisationOptions.Options, whose PropertyNamingPolicy (default JsonNamingPolicy.CamelCase) rewrites bag keys (see DictionaryStringObjectJsonConverter.Write). A key written as "SessionId" therefore comes back as "sessionId" — and the exact-match TryGetValue/Contains missed it.
The fix in the publisher now resolves keys via the active naming policy (ASBConstants.IsBagKey / IsReservedHeader), which is correct under any policy, including a user-overridden one (JsonSerialisationOptions.Options has a public setter).
This issue tracks the other sites with the same pattern. They were left out of the #4054 PR to keep its scope tight, and are categorised below by whether they reproduce today.
Reproducibly broken under the default (camelCase) policy
RocketMqMessageProducer — Bag.TryGetValue(HeaderNames.Keys, …) (RocketMqMessageProducer.cs:156) and HeaderNames.Tag (:172). The constants are "Brighter-RocketMQ-Keys" / "Brighter-RocketMQ-Tag"; camelCase lowercases the leading char (brighter-RocketMQ-Keys), so an exact-match lookup misses the key after an Outbox round-trip. Producer-side, app-supplied keys → genuinely affected.
Latent — correct only by accident under camelCase, would break under another policy (e.g. SnakeCaseLower)
AWS messageDeduplicationId — SnsMessagePublisher.cs:84, SqsMessageSender.cs:100 (plus the .V4 equivalents and AwsScheduler). The constant is already camelCase, so it survives the default policy — but ConvertName under snake_case yields message_deduplication_id and the exact match fails.
Core ProducerTopic — OutboxProducerMediator.cs:946 reads Message.ProducerTopicHeaderName ("paramore.brighter.ProducerTopic"), set in WrapPipeline/WrapPipelineAsync before the Outbox store. Leading char is already lowercase, so it survives camelCase; would break under snake_case.
Same code smell but NOT reproducibly broken
AzureServiceBusScheduler — AzureServiceBusScheduler.cs:135 (ReservedHeaders.Contains(h.Key), no comparer) and :150 (TryGetValue(ASBConstants.SessionIdKey, …)). These look like the [Bug] Do Case insensitive check for ASB sessionID #4054 twin, butConvertToServiceBusMessage runs against a freshly-constructed header whose Bag is empty (the original message is serialized into the body as a FireAzureScheduler payload, not the header bag). So the lookups never mismatch today. Worth hardening for consistency, but no live bug. Note the scheduler has its own duplicate ASBConstants (separate assembly), so it can't share the gateway's helper.
Suggested direction
Provide a shared, naming-policy-aware bag-key lookup (mirroring ASBConstants.IsBagKey) that reads JsonSerialisationOptions.Options.PropertyNamingPolicy at call time and matches raw-or-ConvertName, case-insensitive. Prime candidate for lifting into Paramore.Brighter core so every gateway/transform can reuse it rather than each re-implementing exact-match lookups.
Deeper question worth a design note: reserved/system bag keys (SessionId, etc.) arguably shouldn't be subject to a cosmetic naming policy during persistence at all — but changing the converter is a breaking wire-format change, so the read-side policy-aware lookup is the pragmatic fix.
Background
#4054 / #4205 fixed the ASB
AzureServiceBusMessagePublisher, where aSessionIdbag key was silently dropped after an Outbox round-trip. Root cause: the Outbox serializes the headerBagwithJsonSerialisationOptions.Options, whosePropertyNamingPolicy(defaultJsonNamingPolicy.CamelCase) rewrites bag keys (seeDictionaryStringObjectJsonConverter.Write). A key written as"SessionId"therefore comes back as"sessionId"— and the exact-matchTryGetValue/Containsmissed it.The fix in the publisher now resolves keys via the active naming policy (
ASBConstants.IsBagKey/IsReservedHeader), which is correct under any policy, including a user-overridden one (JsonSerialisationOptions.Optionshas a public setter).This issue tracks the other sites with the same pattern. They were left out of the #4054 PR to keep its scope tight, and are categorised below by whether they reproduce today.
Reproducibly broken under the default (camelCase) policy
RocketMqMessageProducer—Bag.TryGetValue(HeaderNames.Keys, …)(RocketMqMessageProducer.cs:156) andHeaderNames.Tag(:172). The constants are"Brighter-RocketMQ-Keys"/"Brighter-RocketMQ-Tag"; camelCase lowercases the leading char (brighter-RocketMQ-Keys), so an exact-match lookup misses the key after an Outbox round-trip. Producer-side, app-supplied keys → genuinely affected.Latent — correct only by accident under camelCase, would break under another policy (e.g.
SnakeCaseLower)messageDeduplicationId—SnsMessagePublisher.cs:84,SqsMessageSender.cs:100(plus the.V4equivalents andAwsScheduler). The constant is already camelCase, so it survives the default policy — butConvertNameunder snake_case yieldsmessage_deduplication_idand the exact match fails.ProducerTopic—OutboxProducerMediator.cs:946readsMessage.ProducerTopicHeaderName("paramore.brighter.ProducerTopic"), set inWrapPipeline/WrapPipelineAsyncbefore the Outbox store. Leading char is already lowercase, so it survives camelCase; would break under snake_case.Same code smell but NOT reproducibly broken
AzureServiceBusScheduler—AzureServiceBusScheduler.cs:135(ReservedHeaders.Contains(h.Key), no comparer) and:150(TryGetValue(ASBConstants.SessionIdKey, …)). These look like the [Bug] Do Case insensitive check for ASB sessionID #4054 twin, butConvertToServiceBusMessageruns against a freshly-constructed header whoseBagis empty (the original message is serialized into the body as aFireAzureSchedulerpayload, not the header bag). So the lookups never mismatch today. Worth hardening for consistency, but no live bug. Note the scheduler has its own duplicateASBConstants(separate assembly), so it can't share the gateway's helper.Suggested direction
Provide a shared, naming-policy-aware bag-key lookup (mirroring
ASBConstants.IsBagKey) that readsJsonSerialisationOptions.Options.PropertyNamingPolicyat call time and matches raw-or-ConvertName, case-insensitive. Prime candidate for lifting intoParamore.Brightercore so every gateway/transform can reuse it rather than each re-implementing exact-match lookups.Deeper question worth a design note: reserved/system bag keys (
SessionId, etc.) arguably shouldn't be subject to a cosmetic naming policy during persistence at all — but changing the converter is a breaking wire-format change, so the read-side policy-aware lookup is the pragmatic fix.Filed as follow-up to #4205.