[Bug] GET /api/artstudio 500s for any user with pre-#353 asset documents — "Document element is missing for required non-nullable property 'GalleryStatus'" #369

Closed
opened 2026-07-05 00:10:44 +00:00 by spikerj · 1 comment
Owner

Found by the #359 Angular-in-the-loop validation on the Mac stack (2026-07-04).

Symptom

GET /api/artstudio?page=1&pageSize=20 returns 500 and the student sees a permanent "Failed to list art assets" toast + an empty My Assets grid — for every asset, forever:

{"error":"Failed to list art assets","details":"Document element is missing for required non-nullable property 'GalleryStatus'."}

After backfilling galleryStatus the next missing field fails the same way:

{"error":"Failed to list art assets","details":"Document element is missing for required non-nullable property 'FlaggedForReview'."}

Root cause

The list path reads art-assets through the EF Core Mongo mapping, which requires every non-nullable property to be present in the document. Documents created before the #349/#352 (flaggedForReview, quarantinedArtifacts) and #353 (galleryStatus) merges lack those elements, so a single legacy document poisons the whole list query. (The Bson-attribute model SpikerSoft.Data/Mongos/ArtStudio/ArtAsset.cs has C# defaults, but EF's required-property check fires before defaults help.)

On the Mac dev DB, 7 of 13 documents lacked galleryStatus and 3 lacked flaggedForReview/quarantinedArtifacts. Production is exposed the same way: any asset created before those deploys breaks its owner's My Assets list.

Local unblock used (documented in docs/mac-local-stack.md §6)

docker exec mongo-router mongosh spikersoft --quiet --eval \
  'db.getCollection("art-assets").updateMany({galleryStatus:{$exists:false}},{$set:{galleryStatus:"None"}});
   db.getCollection("art-assets").updateMany({flaggedForReview:{$exists:false}},{$set:{flaggedForReview:false}});
   db.getCollection("art-assets").updateMany({quarantinedArtifacts:{$exists:false}},{$set:{quarantinedArtifacts:[]}})'

Proper fix options

  1. Make the EF mapping tolerate missing elements (nullable + coalesce, or configure defaults), and/or
  2. A one-time migration that backfills the three fields, and
  3. A convention/test so future required fields added to ArtAsset ship with a migration (this has now happened twice in one week).

Part of epic #346; found during #359.

Found by the #359 Angular-in-the-loop validation on the Mac stack (2026-07-04). ## Symptom `GET /api/artstudio?page=1&pageSize=20` returns **500** and the student sees a permanent "Failed to list art assets" toast + an empty My Assets grid — for every asset, forever: ``` {"error":"Failed to list art assets","details":"Document element is missing for required non-nullable property 'GalleryStatus'."} ``` After backfilling `galleryStatus` the next missing field fails the same way: ``` {"error":"Failed to list art assets","details":"Document element is missing for required non-nullable property 'FlaggedForReview'."} ``` ## Root cause The list path reads `art-assets` through the EF Core Mongo mapping, which requires every non-nullable property to be present in the document. Documents created **before** the #349/#352 (`flaggedForReview`, `quarantinedArtifacts`) and #353 (`galleryStatus`) merges lack those elements, so a single legacy document poisons the whole list query. (The Bson-attribute model `SpikerSoft.Data/Mongos/ArtStudio/ArtAsset.cs` has C# defaults, but EF's required-property check fires before defaults help.) On the Mac dev DB, 7 of 13 documents lacked `galleryStatus` and 3 lacked `flaggedForReview`/`quarantinedArtifacts`. **Production is exposed the same way**: any asset created before those deploys breaks its owner's My Assets list. ## Local unblock used (documented in docs/mac-local-stack.md §6) ```bash docker exec mongo-router mongosh spikersoft --quiet --eval \ 'db.getCollection("art-assets").updateMany({galleryStatus:{$exists:false}},{$set:{galleryStatus:"None"}}); db.getCollection("art-assets").updateMany({flaggedForReview:{$exists:false}},{$set:{flaggedForReview:false}}); db.getCollection("art-assets").updateMany({quarantinedArtifacts:{$exists:false}},{$set:{quarantinedArtifacts:[]}})' ``` ## Proper fix options 1. Make the EF mapping tolerate missing elements (nullable + coalesce, or configure defaults), and/or 2. A one-time migration that backfills the three fields, and 3. A convention/test so future required fields added to `ArtAsset` ship with a migration (this has now happened twice in one week). Part of epic #346; found during #359.
Author
Owner

Resolved in spikersoft-backend PR #75 (merged to master). Root cause: MongoDB.EntityFrameworkCore throws on any missing non-nullable mapped member (and rejects [BsonDefaultValue]), so the post-P1 FlaggedForReview/GalleryStatus fields 500'd list queries containing legacy docs. Fixed schema-tolerantly (nullable backing fields + normalizing accessors; docs heal on next write; API JSON unchanged) with 11 regression tests driving era-exact BSON through the provider's real pipeline, plus a model-guard test forbidding future post-P1 non-nullables on these entities. Optional prod backfill one-liner is in the PR/report if wanted. Closing.

Resolved in spikersoft-backend PR #75 (merged to `master`). Root cause: MongoDB.EntityFrameworkCore throws on any missing non-nullable mapped member (and rejects `[BsonDefaultValue]`), so the post-P1 `FlaggedForReview`/`GalleryStatus` fields 500'd list queries containing legacy docs. Fixed schema-tolerantly (nullable backing fields + normalizing accessors; docs heal on next write; API JSON unchanged) with 11 regression tests driving era-exact BSON through the provider's real pipeline, plus a model-guard test forbidding future post-P1 non-nullables on these entities. Optional prod backfill one-liner is in the PR/report if wanted. Closing.
Sign in to join this conversation.