janhq/jan · warning · Error

This conversation has no user message to respond to. Add a m

Error message

This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.

What it means

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.

Source

Thrown at web-app/src/lib/custom-chat-transport.ts:1303

        const trimResult = trimMessages(
          messagesToConvert,
          contextConfig,
          systemPromptTokens
        )
        effectiveMessages = trimResult.messages
        if (trimResult.trimmedCount > 0) {
          console.debug(
            `[context-manager] Trimmed ${trimResult.trimmedCount} oldest messages to fit context budget`
          )
        }
      }
    }

    // Many chat templates (Qwen3.5+) reject a window with no genuine user query
    // and throw a cryptic Jinja error. Fail early with a clear message when
    // deletion/eviction has left no real user turn to respond to.
    if (!hasGenuineUserQuery(effectiveMessages)) {
      throw new Error(
        'This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.'
      )
    }

    const modelSupportsVision =
      selectedModel?.capabilities?.includes('vision') ?? false
    const baseMessages = await convertToModelMessages(
      coalesceMessagesForAlternation(
        resolveOrphanToolCalls(
          this.encodeVideoAttachments(
            this.encodeAudioAttachments(
              stripUnsupportedImageParts(
                this.mapUserInlineAttachments(effectiveMessages),
                modelSupportsVision
              )
            )
          )
        )

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Add a new user message before regenerating, or regenerate from a turn whose user question is still in-window.
  2. Increase `max_context_tokens` or disable auto-trim so the original question is retained.
  3. Pin the most recent user turn so trim never removes the last genuine query.
  4. If the turn is media-only, attach a text caption so hasGenuineUserQuery sees it.

Example fix

// before
if (!hasGenuineUserQuery(effectiveMessages)) {
  throw new Error('This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.')
}

// after
if (!hasGenuineUserQuery(effectiveMessages)) {
  // Recover by re-injecting the last known user text from history instead of hard-failing.
  const lastUserText = extractLatestUserText(options.messages)
  if (!lastUserText) {
    throw new Error('This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.')
  }
  effectiveMessages = [
    { id: 'injected-user', role: 'user', content: lastUserText, parts: [{ type: 'text', text: lastUserText }] },
    ...effectiveMessages,
  ]
}
Defensive patterns

Strategy: validation

Validate before calling

// Before sending, ensure a genuine user text turn exists and survives trim.
const userTurns = options.messages.filter((m) => m.role === 'user')
const hasText = userTurns.some((m) =>
  (Array.isArray(m.parts) ? m.parts : []).some((p) => p.type === 'text' && p.text.trim().length > 0)
)
if (!hasText) {
  toast.error('Nothing to respond to', { description: 'Add a text message before sending.' })
  return
}

Type guard

function hasGenuineUserQuery(messages: UIMessage[]): boolean {
  return messages.some(
    (m) => m.role === 'user' &&
      (Array.isArray(m.parts) ? m.parts : []).some(
        (p) => p.type === 'text' && typeof p.text === 'string' && p.text.trim().length > 0
      )
  )
}

Try / catch

if (!hasGenuineUserQuery(effectiveMessages)) {
  // Try to recover the last genuine user text before failing.
  const lastUserText = extractLatestUserText(options.messages)
  if (!lastUserText) {
    throw new Error('This conversation has no user message to respond to. Add a message, or regenerate from a turn that includes your question.')
  }
  effectiveMessages = [injectUserTurn(lastUserText), ...effectiveMessages]
}

Prevention

When it happens

Trigger: `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.

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

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/b5afc636cd5924ff. Report an issue: GitHub.