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
- 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.
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
- 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.
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
- Invalid backend string: ${targetBackendString} supplied to u
- Invalid backend string format: "${targetBackendString}". Exp
- Invalid path or file ${path}
- Failed to parse archive name: ${archiveName}. Expected forma
- Invalid backend archive name: ${archiveName}
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/b5afc636cd5924ff.
Report an issue: GitHub.