From 8d1552e334769096fe10732f6800ffd08b76f904 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Fri, 20 Mar 2026 15:31:00 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B=20Store=20invalid=20UTF-8=20bo?= =?UTF-8?q?dies=20as=20binary=20with=20correct=20content=20type=20metadata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a message body fails UTF-8 decoding despite the content type header claiming text, update the metadata content type to application/octet-stream and fall back to body storage instead of silently dropping the body. --- src/ServiceControl.Audit.Persistence/BodyStorageEnricher.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ServiceControl.Audit.Persistence/BodyStorageEnricher.cs b/src/ServiceControl.Audit.Persistence/BodyStorageEnricher.cs index be9e133bf8..a27635cb39 100644 --- a/src/ServiceControl.Audit.Persistence/BodyStorageEnricher.cs +++ b/src/ServiceControl.Audit.Persistence/BodyStorageEnricher.cs @@ -66,7 +66,9 @@ async ValueTask TryStoreBody(ReadOnlyMemory body, ProcessedMessage p catch (DecoderFallbackException e) { useBodyStore = true; - logger.LogInformation("Body for {BodyId} could not be stored embedded, fallback to body storage ({ErrorMessage})", bodyId, e.Message); + contentType = "application/octet-stream"; + processedMessage.MessageMetadata["ContentType"] = contentType; + logger.LogInformation("Body for {BodyId} is not valid UTF-8 despite content type header, storing as binary ({ErrorMessage})", bodyId, e.Message); } } From a6700007f9152f202a2e89b09b2448986eda0a03 Mon Sep 17 00:00:00 2001 From: Ramon Smits Date: Fri, 20 Mar 2026 18:56:29 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=90=9B=20Apply=20same=20binary=20cont?= =?UTF-8?q?ent=20type=20fix=20to=20error=20ingestion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a failed message body is not valid UTF-8 despite the content type header, update metadata and attachment content type to application/octet-stream. Replay is unaffected as it uses the original message headers, not metadata. --- .../UnitOfWork/RavenRecoverabilityIngestionUnitOfWork.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ServiceControl.Persistence.RavenDB/UnitOfWork/RavenRecoverabilityIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.RavenDB/UnitOfWork/RavenRecoverabilityIngestionUnitOfWork.cs index 2351e78e3e..710e256afa 100644 --- a/src/ServiceControl.Persistence.RavenDB/UnitOfWork/RavenRecoverabilityIngestionUnitOfWork.cs +++ b/src/ServiceControl.Persistence.RavenDB/UnitOfWork/RavenRecoverabilityIngestionUnitOfWork.cs @@ -53,7 +53,8 @@ public Task RecordFailedProcessingAttempt( } catch (ArgumentException) { - // If it won't decode to text, don't index it + contentType = "application/octet-stream"; + processingAttempt.MessageMetadata["ContentType"] = contentType; } } }