{"record":{"id":"b5afc636cd5924ff","repo":"janhq/jan","slug":"this-conversation-has-no-user-message-to-respond-t","errorCode":null,"errorMessage":"This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.","messagePattern":"This conversation has no user message to respond to\\. Add a message, or regenerate from a turn that includes your question\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"web-app/src/lib/custom-chat-transport.ts","lineNumber":1303,"sourceCode":"        const trimResult = trimMessages(\n          messagesToConvert,\n          contextConfig,\n          systemPromptTokens\n        )\n        effectiveMessages = trimResult.messages\n        if (trimResult.trimmedCount > 0) {\n          console.debug(\n            `[context-manager] Trimmed ${trimResult.trimmedCount} oldest messages to fit context budget`\n          )\n        }\n      }\n    }\n\n    // Many chat templates (Qwen3.5+) reject a window with no genuine user query\n    // and throw a cryptic Jinja error. Fail early with a clear message when\n    // deletion/eviction has left no real user turn to respond to.\n    if (!hasGenuineUserQuery(effectiveMessages)) {\n      throw new Error(\n        'This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.'\n      )\n    }\n\n    const modelSupportsVision =\n      selectedModel?.capabilities?.includes('vision') ?? false\n    const baseMessages = await convertToModelMessages(\n      coalesceMessagesForAlternation(\n        resolveOrphanToolCalls(\n          this.encodeVideoAttachments(\n            this.encodeAudioAttachments(\n              stripUnsupportedImageParts(\n                this.mapUserInlineAttachments(effectiveMessages),\n                modelSupportsVision\n              )\n            )\n          )\n        )","sourceCodeStart":1285,"sourceCodeEnd":1321,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/web-app/src/lib/custom-chat-transport.ts#L1285-L1321","documentation":"Thrown after context trimming/compaction when `hasGenuineUserQuery(effectiveMessages)` returns false. The guard exists because chat templates (Qwen3.5+) reject a window with no real user turn and throw a cryptic Jinja error; this fails early with a clear message. Eviction removed, or the window never contained, a substantive user text turn.","triggerScenarios":"`maxContextTokens` triggered `trimMessages`/`compactMessages` and evicted the only user message; the user message is whitespace-only or media-only (no text); regenerating from an assistant turn whose preceding user turn was already trimmed; `hasGenuineUserQuery` only counts roles with non-empty text content.","commonSituations":"Long conversations where auto-trim evicted the original question and the user clicks Regenerate on a later turn; image-only user messages; very small `max_context_tokens` setting; a user message edited to empty string.","solutions":["Add a new user message before regenerating, or regenerate from a turn whose user question is still in-window.","Increase `max_context_tokens` or disable auto-trim so the original question is retained.","Pin the most recent user turn so trim never removes the last genuine query.","If the turn is media-only, attach a text caption so hasGenuineUserQuery sees it."],"exampleFix":"// before\nif (!hasGenuineUserQuery(effectiveMessages)) {\n  throw new Error('This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.')\n}\n\n// after\nif (!hasGenuineUserQuery(effectiveMessages)) {\n  // Recover by re-injecting the last known user text from history instead of hard-failing.\n  const lastUserText = extractLatestUserText(options.messages)\n  if (!lastUserText) {\n    throw new Error('This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.')\n  }\n  effectiveMessages = [\n    { id: 'injected-user', role: 'user', content: lastUserText, parts: [{ type: 'text', text: lastUserText }] },\n    ...effectiveMessages,\n  ]\n}","handlingStrategy":"validation","validationCode":"// Before sending, ensure a genuine user text turn exists and survives trim.\nconst userTurns = options.messages.filter((m) => m.role === 'user')\nconst hasText = userTurns.some((m) =>\n  (Array.isArray(m.parts) ? m.parts : []).some((p) => p.type === 'text' && p.text.trim().length > 0)\n)\nif (!hasText) {\n  toast.error('Nothing to respond to', { description: 'Add a text message before sending.' })\n  return\n}","typeGuard":"function hasGenuineUserQuery(messages: UIMessage[]): boolean {\n  return messages.some(\n    (m) => m.role === 'user' &&\n      (Array.isArray(m.parts) ? m.parts : []).some(\n        (p) => p.type === 'text' && typeof p.text === 'string' && p.text.trim().length > 0\n      )\n  )\n}","tryCatchPattern":"if (!hasGenuineUserQuery(effectiveMessages)) {\n  // Try to recover the last genuine user text before failing.\n  const lastUserText = extractLatestUserText(options.messages)\n  if (!lastUserText) {\n    throw new Error('This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.')\n  }\n  effectiveMessages = [injectUserTurn(lastUserText), ...effectiveMessages]\n}","preventionTips":["Pin the latest genuine user turn so context trimming never removes it.","Increase max_context_tokens or disable auto-trim for long conversations.","Regenerate from a turn whose preceding user question is still in-window.","Attach a text caption to media-only user messages."],"tags":["context","trimming","chat-template","validation"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}