{"record":{"id":"35789d4bd3194677","repo":"jackwener/OpenCLI","slug":"empty-user-message","errorCode":null,"errorMessage":"Empty user message","messagePattern":"Empty user message","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/antigravity/serve.js","lineNumber":351,"sourceCode":"            }\n            throw err;\n        }\n        await sleep(pollInterval);\n    }\n    throw new Error(`Timeout waiting for Antigravity reply after ${timeout / 1000}s`);\n}\n// ─── Request Handlers ────────────────────────────────────────────────\nasync function handleMessages(body, page, opts = {}) {\n    const { bridge, timeout, reconnect } = opts;\n    // Extract the last user message\n    const userMessages = body.messages.filter(m => m.role === 'user');\n    if (userMessages.length === 0) {\n        throw new Error('No user message found in request');\n    }\n    const lastUserMsg = userMessages[userMessages.length - 1];\n    const userText = extractTextContent(lastUserMsg.content);\n    if (!userText.trim()) {\n        throw new Error('Empty user message');\n    }\n    // Optimization 1: New conversation if this is the first message in the session\n    if (body.messages.length === 1) {\n        console.error(`[serve] New session detected (1 message). Starting new conversation in UI.`);\n        await startNewConversation(page);\n    }\n    // Optimization 3: Switch model if requested\n    if (body.model) {\n        await switchModel(page, body.model);\n    }\n    // Get conversation state before sending\n    const beforeText = await getConversationText(page);\n    // Send the message\n    console.error(`[serve] Sending: \"${userText.slice(0, 80)}${userText.length > 80 ? '...' : ''}\"`);\n    await sendMessage(page, userText, bridge);\n    // Poll for reply (change detection)\n    console.error('[serve] Waiting for reply...');\n    page = await waitForReply(page, beforeText, { timeout, reconnect });","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/antigravity/serve.js#L333-L369","documentation":"After finding the last user message, handleMessages runs extractTextContent on its content and throws 'Empty user message' when the resulting text is whitespace-only. A user-role message exists but carries no usable text (e.g. image-only or empty content parts), so there is nothing to type into the Antigravity input.","triggerScenarios":"user message content is an empty string, whitespace, an empty parts array, or extractTextContent drops non-text parts leaving ''.","commonSituations":"Multimodal clients sending only an image attachment the CLI can't type; templating bug leaving the user content blank; content as a parts array with only unsupported types; client sending role 'user' with content null.","solutions":["Send non-empty text in the user message content","Inspect how extractTextContent handles content arrays and include a text part","Fix client templating so the prompt text is actually populated","Reject empty prompts client-side before calling the server"],"exampleFix":"// before\n{ role: 'user', content: [{ type: 'image_url', image_url: { url: '...' } }] }\n// after\n{ role: 'user', content: [{ type: 'text', text: 'Describe this image' }, { type: 'image_url', image_url: { url: '...' } }] }","handlingStrategy":"validation","validationCode":"const text = lastUserMsg.content;\nconst hasText = typeof text === 'string' ? text.trim().length > 0\n  : Array.isArray(text) && text.some(p => p?.type === 'text' && p.text?.trim());\nif (!hasText) throw new Error('User message must include non-empty text');","typeGuard":"function hasNonEmptyText(msg) {\n  const c = msg?.content;\n  return (typeof c === 'string' && c.trim().length > 0)\n    || (Array.isArray(c) && c.some(p => typeof p?.text === 'string' && p.text.trim().length > 0));\n}","tryCatchPattern":"try {\n  const reply = await sendToAntigravity(body);\n} catch (err) {\n  if (err.message === 'Empty user message') {\n    console.error('User content must contain text, got:', JSON.stringify(body.messages.at(-1)?.content));\n  }\n  throw err;\n}","preventionTips":["Include a text part alongside images in multimodal prompts","Never send whitespace-only prompts","Check extractTextContent's supported content shapes","Reject empty prompts in your client UI before sending"],"tags":["validation","request-body","empty-content","antigravity"],"backgroundTag":"missing-request-parameter","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}