micro/go-micro · warning
flow: LLMOptimizer returned an empty prompt
Error message
flow: LLMOptimizer returned an empty prompt
What it means
The model responded successfully to the prompt-revision request, but its Answer (and fallback Reply) contained only whitespace or was empty. The library refuses to return an empty proposal since replacing a prompt with empty text would break the workflow step.
Source
Thrown at flow/analyze.go:171
// OptimizePrompt asks the model for a revised prompt for candidate using the
// current prompt and trace feedback. It returns only the proposal; it never
// modifies a Flow, Step, or Checkpoint.
func (o *PromptOptimizer) OptimizePrompt(ctx context.Context, candidate Candidate, currentPrompt string) (string, error) {
if o == nil || o.model == nil {
return "", fmt.Errorf("flow: LLMOptimizer requires a model")
}
prompt := fmt.Sprintf("Revise this workflow step prompt to improve the failing step.\nStep: %s\nMetric: %s\nScore: %.2f\nFeedback:\n- %s\n\nCurrent prompt:\n%s\n\nReturn only the revised prompt.", candidate.Step, candidate.Metric, candidate.Score, strings.Join(candidate.SampleFeedback, "\n- "), currentPrompt)
resp, err := o.model.Generate(ctx, &ai.Request{Prompt: prompt})
if err != nil {
return "", err
}
proposal := strings.TrimSpace(resp.Answer)
if proposal == "" {
proposal = strings.TrimSpace(resp.Reply)
}
if proposal == "" {
return "", fmt.Errorf("flow: LLMOptimizer returned an empty prompt")
}
return proposal, nil
}
func verificationFields(result string) (bool, string, bool) {
if result == "" {
return false, "", false
}
var obj map[string]any
if err := json.Unmarshal([]byte(result), &obj); err != nil {
return false, "", false
}
v, ok := obj["verification_passed"].(bool)
if !ok {
return false, "", false
}
fb, _ := obj["verification_feedback"].(string)
return v, fb, trueView on GitHub (pinned to 24529f1404)
Solutions
- Check the model response fields your provider actually populates (Answer vs Reply) and confirm the adapter maps them.
- Increase max tokens / reduce prompt size so the model can produce output.
- Switch to a stronger model or retry the Generate call; transient empty completions are common.
- Log resp.Answer and resp.Reply to confirm what the model returned before failing.
Example fix
// before
resp, err := o.model.Generate(ctx, &ai.Request{Prompt: prompt}) // tiny model, max_tokens:1
// after
resp, err := o.model.Generate(ctx, &ai.Request{Prompt: prompt, Options: map[string]any{"max_tokens": 512}}) Defensive patterns
Strategy: retry
Validate before calling
if resp != nil && strings.TrimSpace(resp.Answer) == "" && strings.TrimSpace(resp.Reply) == "" {
return errors.New("model returned empty answer; retrying")
} Type guard
func hasProposal(resp *ai.Response) bool {
return resp != nil && (strings.TrimSpace(resp.Answer) != "" || strings.TrimSpace(resp.Reply) != "")
} Try / catch
prompt, err := opt.OptimizePrompt(ctx, cand, current)
if err != nil && strings.Contains(err.Error(), "empty prompt") {
prompt, err = opt.OptimizePrompt(ctx, cand, current) // one retry on transient empty completion
if err != nil { prompt = current }
} Prevention
- Set adequate max_tokens for revision requests.
- Prefer models known to produce non-empty structured output.
- Log raw model responses to detect providers that populate alternate fields.
When it happens
Trigger: Calling OptimizePrompt when the model returns an empty string — e.g. the model hit a content filter, the response was truncated to nothing, or a misconfigured provider returned a 200 with no body content.
Common situations: Using a provider that populates a different response field than Answer/Reply; very small models that emit blank output; token limits causing empty completions; proxy/gateway stripping the response body.
Related errors
- flow: LLMOptimizer requires a model
- unknown provider: %s
- discover tools: %w
- flow: LLMGrader returned an empty grade
- ErrStreamingUnsupported
AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01).
Data as JSON: /api/errors/015f7e8e8ca3387a.
Report an issue: GitHub.