Tencent/WeKnora · warning
decode suggestion JSON: %w
Error message
decode suggestion JSON: %w
What it means
parseGeneratedSuggestions extracts a JSON envelope from raw LLM output and unmarshals it. If the extracted substring is not valid JSON (malformed model output), json.Unmarshal fails and the error is wrapped as 'decode suggestion JSON'. This is a model-output quality problem, not a caller bug.
Source
Thrown at internal/application/service/message_suggestion.go:815
}
type generatedSuggestionEnvelope struct {
Questions []struct {
Text string `json:"text"`
Category string `json:"category"`
} `json:"questions"`
}
func parseGeneratedSuggestions(content string, allowedCategories []string, limit int) (types.SuggestionItems, error) {
content = strings.TrimSpace(suggestionThinkBlock.ReplaceAllString(content, ""))
start := strings.Index(content, "{")
end := strings.LastIndex(content, "}")
if start < 0 || end < start {
return nil, errors.New("model returned invalid suggestion JSON")
}
var envelope generatedSuggestionEnvelope
if err := json.Unmarshal([]byte(content[start:end+1]), &envelope); err != nil {
return nil, fmt.Errorf("decode suggestion JSON: %w", err)
}
allowed := make(map[string]struct{}, len(allowedCategories))
for _, category := range allowedCategories {
allowed[category] = struct{}{}
}
seen := make(map[string]struct{})
items := make(types.SuggestionItems, 0, limit)
for _, question := range envelope.Questions {
text := strings.TrimSpace(question.Text)
if text == "" || len([]rune(text)) > 200 {
continue
}
key := normalizeSuggestionText(text)
if _, exists := seen[key]; exists {
continue
}
seen[key] = struct{}{}
category := question.CategoryView on GitHub (pinned to 988cbb0330)
Solutions
- Strengthen the prompt: require strict JSON, no fences, no commentary, and set a sufficient max_tokens.
- Use structured output / JSON mode if the model provider supports it, or lower temperature.
- Log the raw content on failure to see what the model actually returned, then widen the extraction or repair (e.g. strip fences) before unmarshaling.
- Retry the generation once on decode failure; treat repeated failures as model regression.
Example fix
// before
if err := json.Unmarshal([]byte(content[start:end+1]), &envelope); err != nil {
return nil, fmt.Errorf("decode suggestion JSON: %w", err)
}
// after
candidate := strings.ReplaceAll(content[start:end+1], "```", "")
if err := json.Unmarshal([]byte(candidate), &envelope); err != nil {
return nil, fmt.Errorf("decode suggestion JSON: %w (content: %.200s)", err, content)
} Defensive patterns
Strategy: validation
Validate before calling
start := strings.Index(content, "{")
end := strings.LastIndex(content, "}")
if start < 0 || end <= start { return fmt.Errorf("no JSON in model output") }
json.Valid([]byte(content[start : end+1])) // cheap pre-check Type guard
func looksLikeSuggestionJSON(content string) bool {
s := strings.Index(content, "{"); e := strings.LastIndex(content, "}")
return s >= 0 && e > s && json.Valid([]byte(content[s:e+1]))
} Try / catch
suggestions, err := parseGeneratedSuggestions(content, allowed)
if err != nil && strings.Contains(err.Error(), "decode suggestion JSON") {
log.Warnf("bad model output: %.200s", content)
suggestions, err = retryGeneration(ctx) // one retry
} Prevention
- Prompt for strict JSON with no fences or commentary
- Set max_tokens high enough to avoid truncation
- Prefer provider JSON/structured-output mode; log raw outputs on failure
When it happens
Trigger: generateWithModel receiving model content where the text between the first '{' and last '}' is not valid JSON — truncated response, prose mixed with JSON, markdown fences inside the braces, or non-JSON hallucination.
Common situations: Model hitting max_tokens and truncating the JSON; model wrapping JSON in ```json fences; small/weak models producing comments or trailing commas; temperature too high for structured output.
Related errors
- failed to unmarshal response: %w
- failed to unmarshal response: %w
- LLM returned nil response
- args must be a string or an array of strings: %w
- logical operator %s requires an array of conditions
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/dfb1de9e25468aa7.
Report an issue: GitHub.