nexu-io/open-design · error · Error

invalid comment status

Error message

invalid comment status

What it means

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.

Source

Thrown at apps/daemon/src/db.ts:3055

 * table-wide renumber, and never touches `pin_seq`.
 */
export function reorderPreviewComment(
  db: SqliteDb,
  projectId: string,
  conversationId: string,
  id: string,
  sortKey: number,
) {
  db.prepare(
    `UPDATE preview_comments
        SET sort_key = ?
      WHERE id = ? AND project_id = ? AND conversation_id = ?`,
  ).run(sortKey, id, projectId, conversationId);
  return getPreviewComment(db, projectId, conversationId, id);
}

export function updatePreviewCommentStatus(db: SqliteDb, projectId: string, conversationId: string, id: string, status: string) {
  if (!PREVIEW_COMMENT_STATUSES.has(status)) throw new Error('invalid comment status');
  const now = Date.now();
  db.prepare(
    `UPDATE preview_comments
        SET status = ?, updated_at = ?
      WHERE id = ? AND project_id = ? AND conversation_id = ?`,
  ).run(status, now, id, projectId, conversationId);
  return getPreviewComment(db, projectId, conversationId, id);
}

/**
 * Team collaboration drift-ladder write-back: persist how a comment resolved this render.
 * `lastGoodPosition`/`anchoredVersion` are COALESCEd so a `lost` resolve (which omits
 * them) keeps the last known-good values instead of wiping them. Does not bump
 * `updated_at` — anchor resolution is a derived read, not a content edit.
 */
export function updatePreviewCommentAnchor(
  db: SqliteDb,
  projectId: string,

View on GitHub (pinned to 5be4028344)

Solutions

  1. Use one of the allowed statuses exactly: 'open', 'attached', 'applying', 'needs_review', 'resolved', 'failed'.
  2. 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).
  3. Normalize/case-fold the input before calling if your source uses different casing.

Example fix

// before: wrong casing / unknown status
updatePreviewCommentStatus(db, p, c, id, 'Resolved');

// after: exact value from PREVIEW_COMMENT_STATUSES
const status = 'resolved'; // 'open' | 'attached' | 'applying' | 'needs_review' | 'resolved' | 'failed'
updatePreviewCommentStatus(db, p, c, id, status);
Defensive patterns

Strategy: validation

Validate before calling

const PREVIEW_COMMENT_STATUSES = new Set(['open','attached','applying','needs_review','resolved','failed']);
if (!PREVIEW_COMMENT_STATUSES.has(status)) {
  throw new Error(`invalid comment status: ${status}`);
}

Type guard

function isPreviewCommentStatus(s: string): s is 'open'|'attached'|'applying'|'needs_review'|'resolved'|'failed' {
  return PREVIEW_COMMENT_STATUSES.has(s);
}

Try / catch

try { updatePreviewCommentStatus(db, p, c, id, status); }
catch (e) {
  if (e instanceof Error && /invalid comment status/.test(e.message)) {
    // normalize status and retry, or reject the client request with 400
  } else throw e;
}

Prevention

When it happens

Trigger: Calling updatePreviewCommentStatus with a status string outside the allowed set — typo, wrong casing, or a state machine value from an incompatible version.

Common situations: 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.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/9b1fdb52de5a066d. Report an issue: GitHub.