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
- Provide non-empty note text, OR at least one image attachment in the attachments array.
- When editing, pass attachments explicitly if you want to clear the note (and vice-versa).
- 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
- Disable the submit button until the comment has a note or an attachment.
- Validate (note?.trim() || attachments.length > 0) client-side before POST.
- When editing, pass both note and attachments explicitly to avoid clearing one accidentally.
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
- invalid comment status
- ${task.folder}: spritesheet too small (${bytes.length} bytes
- ${task.folder}: spritesheet is not webp/png/gif
- zip entry size mismatch: ${relPath}
- invalid zip central directory
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/0aa0ff6d1a66999c.
Report an issue: GitHub.