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

  1. Inspect request.messages and request.variables: ensure at least one message yields non-empty content after substitution
  2. Provide all required variables in request.variables
  3. 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

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


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/300339b4c1d2a82b. Report an issue: GitHub.