linshenkx/prompt-optimizer · error · TestError
No valid messages after processing
Error message
No valid messages after processing
What it means
Thrown after TemplateProcessor.processConversationMessages returns an empty array — i.e., every message was filtered out during variable substitution/processing. The service deliberately refuses to send an empty conversation to the LLM.
Source
Thrown at packages/core/src/services/prompt/service.ts:1129
}
if (!request.messages || request.messages.length === 0) {
throw new TestError("", "", "At least one message is required");
}
// 验证模型存在
const modelConfig = await this.modelManager.getModel(request.modelKey);
if (!modelConfig) {
throw new TestError("", "", "Model not found");
}
// 处理会话消息:替换变量
const processedMessages = TemplateProcessor.processConversationMessages(
request.messages,
request.variables,
);
if (processedMessages.length === 0) {
throw new TestError("", "", "No valid messages after processing");
}
// 使用流式发送,根据是否有工具选择不同的方法
if (request.tools && request.tools.length > 0) {
// 🆕 使用支持工具的流式发送
await this.llmService.sendMessageStreamWithTools(
processedMessages,
request.modelKey,
request.tools,
{
onToken: callbacks.onToken,
onReasoningToken: callbacks.onReasoningToken,
onToolCall: callbacks.onToolCall, // 🆕 传递工具调用回调
onComplete: async (response) => {
if (response) {
console.log(
"[PromptService] Custom conversation test with tools completed successfully",
);View on GitHub (pinned to 3e677b1d9f)
Solutions
- Inspect request.messages and request.variables: ensure at least one message yields non-empty content after substitution
- Provide all required variables in request.variables
- Debug by calling TemplateProcessor.processConversationMessages directly and logging the result to see which messages are dropped
Example fix
// before
await svc.testCustomConversationStream({ modelKey, messages: [{ role: 'user', content: '{{query}}' }], variables: {} });
// after
const vars = { query: 'hello' };
const processed = TemplateProcessor.processConversationMessages(msgs, vars);
if (processed.length === 0) throw new Error('All messages empty after substitution');
await svc.testCustomConversationStream({ modelKey, messages: msgs, variables: vars }); Defensive patterns
Strategy: validation
Validate before calling
const processed = TemplateProcessor.processConversationMessages(req.messages, req.variables ?? {});
if (processed.length === 0) {
throw new Error('No messages survive template processing — check variables and message content');
}
await svc.testCustomConversationStream(req); Type guard
const hasContent = (msgs: Msg[]): msgs is [Msg, ...Msg[]] => TemplateProcessor.processConversationMessages(msgs, vars).length > 0;
Try / catch
try { await svc.testCustomConversationStream(req); } catch (e) { if (e instanceof TestError && /No valid messages/.test(e.message)) { /* fix variables, show editor hint */ } else throw e; } Prevention
- Default missing variables to a placeholder instead of empty string in templates
- Live-preview rendered message content in the prompt editor
- Require at least one non-empty user message in the UI before enabling test
When it happens
Trigger: Passing messages whose content is empty after variable substitution, or messages of types that TemplateProcessor drops, so processedMessages.length === 0. Also happens if request.messages itself is empty (the earlier guard only checks a raw emptiness path).
Common situations: Template references variables not supplied, causing content to resolve to empty string; all messages are system/whitespace-only; overly aggressive message filtering in a custom TemplateProcessor.
Related errors
- Iteration failed: Template not found or invalid
- Template must be a non-empty string
- CONTEXT_ERROR_CODES.STORAGE_ERROR
- CONTEXT_ERROR_CODES.IMPORT_FORMAT_ERROR
- DATA_ERROR_CODES.ELECTRON_API_UNAVAILABLE
AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27).
Data as JSON: /api/errors/300339b4c1d2a82b.
Report an issue: GitHub.