Bugfix: Theme persistence failing with MongoDB 500 error on POST /ap...

Documentation for Sessions

🐛 Bugfix: Theme persistence failing with MongoDB 500 error on POST /ap...

Date: 2026-02-25
Time: 20:18:21 UTC
Session: gspace1-141.9
Agent: backend
Confidence: 98%
Status: ✅ Documented


📋 Decision Context

Question:

Theme persistence failing with MongoDB 500 error on POST /api/spaces/:spaceId/themes — what is the root cause and fix?

Task: Fix theme color persistence 500 errors in backend-api


🎯 Decision Made

Chosen:

Strip both _id AND createdAt from $set payload to prevent $set/$setOnInsert field conflict

Options Considered:

  1. Strip _id from $set payload (already attempted in uncommitted changes but insufficient)
  2. Strip both _id AND createdAt from $set payload to prevent $set/$setOnInsert field conflict
  3. Remove $setOnInsert entirely and always write createdAt via $set
  4. Use replaceOne instead of updateOne to avoid operator conflicts

💭 Reasoning

ROOT CAUSE: When a theme document was fetched from MongoDB (e.g. via GET /api/themes/:id), it included createdAt (set on first insert). When that document was spread into normalizedTheme and then into $set, the createdAt field appeared in BOTH $set (from the spread) and $setOnInsert (explicitly set as a fallback for new docs). MongoDB forbids the same field path in both $set and $setOnInsert — this throws MongoServerError: 'Updating the path createdAt would create a conflict at createdAt'. The error was SILENT from the user's perspective because the frontend catchError handler swallowed it, logging only a warning. The fix destructures createdAt alongside _id before building the $set payload: const { _id, createdAt, ...payloadForSet } = normalizedTheme. This ensures createdAt is ONLY in $setOnInsert (set on insert, preserved on update). The same pattern was applied in ThemesService.upsertTheme() and SpacesService.saveThemeToSpace() for consistency. WHY OTHER OPTIONS ARE WORSE: (1) Removing $setOnInsert loses the creation timestamp on new documents. (2) replaceOne would overwrite the entire document including fields the frontend didn't send, causing data loss. EARLY WARNING SIGNAL: Any MongoDB upsert that spreads a document fetched from the DB into $set while also using $setOnInsert is vulnerable to this conflict. The pattern 'const { _id, createdAt, ...forSet } = doc' should become standard for all upsert operations.


📁 Files Involved


🔗 Related


Auto-generated by MCP Smart Consolidation