{"record":{"id":"9b1fdb52de5a066d","repo":"nexu-io/open-design","slug":"invalid-comment-status","errorCode":null,"errorMessage":"invalid comment status","messagePattern":"invalid comment status","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/db.ts","lineNumber":3055,"sourceCode":" * table-wide renumber, and never touches `pin_seq`.\n */\nexport function reorderPreviewComment(\n  db: SqliteDb,\n  projectId: string,\n  conversationId: string,\n  id: string,\n  sortKey: number,\n) {\n  db.prepare(\n    `UPDATE preview_comments\n        SET sort_key = ?\n      WHERE id = ? AND project_id = ? AND conversation_id = ?`,\n  ).run(sortKey, id, projectId, conversationId);\n  return getPreviewComment(db, projectId, conversationId, id);\n}\n\nexport function updatePreviewCommentStatus(db: SqliteDb, projectId: string, conversationId: string, id: string, status: string) {\n  if (!PREVIEW_COMMENT_STATUSES.has(status)) throw new Error('invalid comment status');\n  const now = Date.now();\n  db.prepare(\n    `UPDATE preview_comments\n        SET status = ?, updated_at = ?\n      WHERE id = ? AND project_id = ? AND conversation_id = ?`,\n  ).run(status, now, id, projectId, conversationId);\n  return getPreviewComment(db, projectId, conversationId, id);\n}\n\n/**\n * Team collaboration drift-ladder write-back: persist how a comment resolved this render.\n * `lastGoodPosition`/`anchoredVersion` are COALESCEd so a `lost` resolve (which omits\n * them) keeps the last known-good values instead of wiping them. Does not bump\n * `updated_at` — anchor resolution is a derived read, not a content edit.\n */\nexport function updatePreviewCommentAnchor(\n  db: SqliteDb,\n  projectId: string,","sourceCodeStart":3037,"sourceCodeEnd":3073,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/db.ts#L3037-L3073","documentation":"Thrown by updatePreviewCommentStatus() when the status argument is not in PREVIEW_COMMENT_STATUSES — the fixed set {'open','attached','applying','needs_review','resolved','failed'}. It is an enum guard at the persistence boundary so the DB never stores an arbitrary string in preview_comments.status. Note: the column is TEXT, so without this check a bad value would silently persist.","triggerScenarios":"Calling updatePreviewCommentStatus with a status string outside the allowed set — typo, wrong casing, or a state machine value from an incompatible version.","commonSituations":"Client sent 'resolved-' or 'Resolved' (casing); a new status was added to the UI but not to PREVIEW_COMMENT_STATUSES; a stale client from an older version sends a legacy status name no longer allowed.","solutions":["Use one of the allowed statuses exactly: 'open', 'attached', 'applying', 'needs_review', 'resolved', 'failed'.","If a new status is genuinely needed, add it to PREVIEW_COMMENT_STATUSES in apps/daemon/src/db.ts first (and to the contracts/UI enum).","Normalize/case-fold the input before calling if your source uses different casing."],"exampleFix":"// before: wrong casing / unknown status\nupdatePreviewCommentStatus(db, p, c, id, 'Resolved');\n\n// after: exact value from PREVIEW_COMMENT_STATUSES\nconst status = 'resolved'; // 'open' | 'attached' | 'applying' | 'needs_review' | 'resolved' | 'failed'\nupdatePreviewCommentStatus(db, p, c, id, status);","handlingStrategy":"validation","validationCode":"const PREVIEW_COMMENT_STATUSES = new Set(['open','attached','applying','needs_review','resolved','failed']);\nif (!PREVIEW_COMMENT_STATUSES.has(status)) {\n  throw new Error(`invalid comment status: ${status}`);\n}","typeGuard":"function isPreviewCommentStatus(s: string): s is 'open'|'attached'|'applying'|'needs_review'|'resolved'|'failed' {\n  return PREVIEW_COMMENT_STATUSES.has(s);\n}","tryCatchPattern":"try { updatePreviewCommentStatus(db, p, c, id, status); }\ncatch (e) {\n  if (e instanceof Error && /invalid comment status/.test(e.message)) {\n    // normalize status and retry, or reject the client request with 400\n  } else throw e;\n}","preventionTips":["Keep the UI/contract status enum in lockstep with PREVIEW_COMMENT_STATUSES.","Reject unknown status values at the API boundary with 400 before they reach the DB layer.","Add a test that the column never persists a value outside the set."],"tags":["db","comments","validation","enum"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}