[Backend][Prod] GetPlatformAdoption crashes: FormatException 'Cannot deserialize a String from BsonType Array' on user-submitted-code #466

Closed
opened 2026-07-11 02:05:18 +00:00 by spikerj · 1 comment
Owner

Summary

GET /api/activity/adoption (GetPlatformAdoptionQueryCollectSubmittedCodeAsync) throws in production:

System.FormatException: Cannot deserialize a 'String' from BsonType 'Array'.
   at MongoDB.EntityFrameworkCore.Serializers.ValueConverterSerializer`2.Deserialize(...)
   at MongoDB.EntityFrameworkCore.Query.QueryingEnumerable`2.Enumerator.MoveNextAsync()

(Version 2.1.5, Production-Main)

Root cause

Serialization contract mismatch on UserSubmittedCode.TestResults (List<TestResult>):

  • Write pathSpikerSoft.EventHandlers.CodeExecution.Services.UserSubmittedCodeService uses the raw MongoDB driver (IMongoCollection<UserSubmittedCode>), which stores TestResults as a native embedded BSON array of subdocuments. This is how every graded submission is written.
  • Read pathSpikerDbContext.ConfigureUserSubmittedCodeEntity had a HasConversion that serialized/deserialized TestResults as a JSON string. When EF materializes a driver-written row it tries to read the array element as a String and throws.

Nothing in the codebase ever wrote a JSON string, so the converter was dead-on-arrival and crashed on all real data.

Blast radius

Two EF read sites materialize UserSubmittedCode and hit this:

  1. GetPlatformAdoptionQueryHandler.CollectSubmittedCodeAsync (the reported endpoint).
  2. ProcessSonarQubeWebhookCommandHandler (FirstOrDefaultAsync(x => x.GitCommit == ...)).

Fix

Remove the JSON-string HasConversion and map TestResults as an owned embedded collection (entity.OwnsMany(e => e.TestResults)) so EF reads the same native array shape the driver writes. Regression covered by an integration test that writes via the driver and reads back through EF.

## Summary `GET /api/activity/adoption` (`GetPlatformAdoptionQuery` → `CollectSubmittedCodeAsync`) throws in production: ``` System.FormatException: Cannot deserialize a 'String' from BsonType 'Array'. at MongoDB.EntityFrameworkCore.Serializers.ValueConverterSerializer`2.Deserialize(...) at MongoDB.EntityFrameworkCore.Query.QueryingEnumerable`2.Enumerator.MoveNextAsync() ``` (Version 2.1.5, Production-Main) ## Root cause Serialization contract mismatch on `UserSubmittedCode.TestResults` (`List<TestResult>`): - **Write path** — `SpikerSoft.EventHandlers.CodeExecution.Services.UserSubmittedCodeService` uses the raw MongoDB driver (`IMongoCollection<UserSubmittedCode>`), which stores `TestResults` as a **native embedded BSON array** of subdocuments. This is how *every* graded submission is written. - **Read path** — `SpikerDbContext.ConfigureUserSubmittedCodeEntity` had a `HasConversion` that serialized/deserialized `TestResults` as a **JSON string**. When EF materializes a driver-written row it tries to read the array element as a `String` and throws. Nothing in the codebase ever wrote a JSON string, so the converter was dead-on-arrival and crashed on all real data. ## Blast radius Two EF read sites materialize `UserSubmittedCode` and hit this: 1. `GetPlatformAdoptionQueryHandler.CollectSubmittedCodeAsync` (the reported endpoint). 2. `ProcessSonarQubeWebhookCommandHandler` (`FirstOrDefaultAsync(x => x.GitCommit == ...)`). ## Fix Remove the JSON-string `HasConversion` and map `TestResults` as an owned embedded collection (`entity.OwnsMany(e => e.TestResults)`) so EF reads the same native array shape the driver writes. Regression covered by an integration test that writes via the driver and reads back through EF.
Author
Owner

Resolved in spikersoft-backend PR #199 (merged): UserSubmittedCode.TestResults read as native BSON array instead of a JSON string — GET /api/activity/adoption no longer throws FormatException. Closing.

Resolved in spikersoft-backend PR #199 (merged): UserSubmittedCode.TestResults read as native BSON array instead of a JSON string — GET /api/activity/adoption no longer throws FormatException. Closing.
Sign in to join this conversation.