nexu-io/open-design · error · Error

comment note required

Error message

comment note required

What it means

Thrown by upsertPreviewComment() when a comment being inserted/updated has no note text AND zero image attachments. The invariant: a preview comment must carry either text or at least one image; an empty comment is rejected as invalid. Existing comments being edited also pass through this gate (attachments default to existingAttachments when not provided, so clearing both note and attachments on edit trips it too).

Source

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

    typeof input?.id === 'string' && input.id.trim()
      ? input.id.trim()
      : null;
  const now = Date.now();
  const existing = requestedId
    ? db
        .prepare(
          `SELECT id, created_at AS createdAt, attachments_json AS attachmentsJson
             FROM preview_comments
            WHERE id = ? AND project_id = ? AND conversation_id = ?`,
        )
        .get(requestedId, projectId, conversationId) as DbRow | undefined
    : undefined;
  const id = existing?.id ?? requestedId ?? randomCommentId();
  const createdAt = existing?.createdAt ?? now;
  const existingAttachments = normalizePreviewCommentAttachments(parseJsonOrUndef(existing?.attachmentsJson));
  const attachments = attachmentsProvided ? incomingAttachments : existingAttachments;
  // A comment must carry either a note or at least one image attachment.
  if (!note && attachments.length === 0) throw new Error('comment note required');
  // pin_seq / pin_seq_confirmed / sort_key are assigned exactly once, on the
  // INSERT branch, and are absent from the ON CONFLICT SET clause below so an
  // edit (existing !== undefined) never rewrites them — see
  // recvq5BVsolIxi / UpsertPreviewCommentOptions above. Computed against THIS
  // db file only: safe as the initial guess even when a sibling device
  // concurrently computes the same number for its own new comment, because a
  // team-shared project's pin_seq_confirmed=0 row gets reconciled to the
  // collab-cloud's globally-serialized seq by confirmPreviewCommentPinSeq
  // once its push resolves (never by recomputing locally again).
  let pinSeq: number | null = null;
  let sortKey: number | null = null;
  let pinSeqConfirmed = 1;
  if (!existing) {
    const pinScope = db
      .prepare(
        `SELECT COALESCE(MAX(pin_seq), 0) AS maxPinSeq
           FROM preview_comments
          WHERE project_id = ? AND file_path = ?`,

View on GitHub (pinned to 5be4028344)

Solutions

  1. Provide non-empty note text, OR at least one image attachment in the attachments array.
  2. When editing, pass attachments explicitly if you want to clear the note (and vice-versa).
  3. Validate client-side before POST: require (note?.trim() || attachments.length > 0).

Example fix

// before: empty comment
upsertPreviewComment(db, p, c, { note: '', attachments: [] });

// after: require note or at least one attachment
if (!note?.trim() && attachments.length === 0) {
  throw new Error('comment needs a note or an attachment');
}
upsertPreviewComment(db, p, c, { note, attachments });
Defensive patterns

Strategy: validation

Validate before calling

function validateCommentInput(note: string | undefined, attachments: unknown[]) {
  if (!note?.trim() && attachments.length === 0) {
    throw new Error('comment needs a note or at least one attachment');
  }
}

Type guard

function commentHasContent(note: string | undefined, attachments: unknown[]): boolean {
  return !!note?.trim() || attachments.length > 0;
}

Try / catch

try { upsertPreviewComment(db, p, c, input); }
catch (e) {
  if (e instanceof Error && /comment note required/.test(e.message)) {
    // surface 'note or attachment required' to the UI; do not silent-drop
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the comment upsert with an empty/whitespace note and no attachments array (or an empty one); editing a comment and stripping both the text and all attachments.

Common situations: UI submitted before the user typed anything; an agent created a pin-only comment without attaching a screenshot; an edit request cleared the note but did not preserve existing attachments.

Related errors


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