{"record":{"id":"cf2d437d7dd2187a","repo":"Tencent/WeKnora","slug":"failed-to-generate-questions-w","errorCode":null,"errorMessage":"failed to generate questions: %w","messagePattern":"failed to generate questions: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/knowledge_process.go","lineNumber":2144,"sourceCode":"\t\t\"doc_name\":       docName,\n\t\t\"language\":       langName,\n\t})\n\tprompt = types.AppendCustomPromptInstructions(prompt, customInstructions, \"question_generation\")\n\n\tthinking := false\n\tmodelCtx := types.WithLLMCallMetadata(ctx, \"question_generation\", \"\")\n\tresponse, err := chatModel.Chat(modelCtx, []chat.Message{\n\t\t{\n\t\t\tRole:    \"user\",\n\t\t\tContent: prompt,\n\t\t},\n\t}, &chat.ChatOptions{\n\t\tTemperature: 0.7,\n\t\tMaxTokens:   512,\n\t\tThinking:    &thinking,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to generate questions: %w\", err)\n\t}\n\n\t// Parse response\n\tlines := strings.Split(response.Content, \"\\n\")\n\tquestions := make([]string, 0, questionCount)\n\tfor _, line := range lines {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tline = strings.TrimLeft(line, \"0123456789.-*) \")\n\t\tline = strings.TrimSpace(line)\n\t\tif line != \"\" && len(line) > 5 {\n\t\t\tquestions = append(questions, line)\n\t\t\tif len(questions) >= questionCount {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}","sourceCodeStart":2126,"sourceCodeEnd":2162,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/knowledge_process.go#L2126-L2162","documentation":"Returned by generateQuestionsWithContext when the chatModel.Chat call itself fails after the prompt was built and sent (temperature 0.7, MaxTokens 512, thinking disabled). The LLM/provider error is wrapped with %w — network timeouts, provider 4xx/5xx, context-length overflow, or content-filter rejections all land here. Callers typically warn and skip the chunk rather than aborting the whole run.","triggerScenarios":"chatModel.Chat(modelCtx, []chat.Message{{Role: \"user\", Content: prompt}}, opts) returns an error — provider API unreachable/timeout, invalid API key, rate limit, prompt exceeds model context window (chunk + surrounding context + template), or provider rejected the request.","commonSituations":"LLM provider outage or degraded latency causing timeouts; API key rotated/revoked; large chunks with image OCR enrichment push the prompt over the context limit; provider rate limits under parallel batch load; model temporarily unavailable.","solutions":["Inspect the wrapped error: timeout vs auth vs rate-limit vs context-length","For context-length errors, truncate the chunk/context section or reduce MaxTokens usage and retry","Check provider status, API key validity, and rate-limit quota","Add per-call retry with backoff for transient provider errors (callers currently just skip the chunk)","Reduce parallelism of batch tasks if hitting provider rate limits"],"exampleFix":"// before: single attempt, chunk skipped on any error\nresponse, err := chatModel.Chat(modelCtx, []chat.Message{{Role: \"user\", Content: prompt}}, opts)\nif err != nil {\n\treturn nil, fmt.Errorf(\"failed to generate questions: %w\", err)\n}\n// after: bounded retry for transient provider errors\nvar response *chat.Response\nvar lastErr error\nfor attempt := 0; attempt < 3; attempt++ {\n\tresponse, lastErr = chatModel.Chat(modelCtx, []chat.Message{{Role: \"user\", Content: prompt}}, opts)\n\tif lastErr == nil {\n\t\tbreak\n\t}\n\tif !isTransientLLMError(lastErr) {\n\t\tbreak\n\t}\n\ttime.Sleep(time.Duration(1<<attempt) * time.Second)\n}\nif lastErr != nil {\n\treturn nil, fmt.Errorf(\"failed to generate questions: %w\", lastErr)\n}","handlingStrategy":"try-catch","validationCode":"// preflight the call: prompt size and model availability\nif len(prompt) > maxPromptChars {\n\treturn fmt.Errorf(\"prompt too large for model %s: %d chars\", modelID, len(prompt))\n}\nif chatModel == nil {\n\treturn fmt.Errorf(\"chat model not initialized\")\n}","typeGuard":"func llmCallReady(chatModel chat.Chat, prompt string) bool {\n\treturn chatModel != nil && strings.TrimSpace(prompt) != \"\"\n}","tryCatchPattern":"response, err := chatModel.Chat(modelCtx, msgs, &chat.ChatOptions{Temperature: 0.7, MaxTokens: 512, Thinking: &thinking})\nif err != nil {\n\tswitch {\n\tcase isContextLengthError(err):\n\t\t// shrink context section and retry once\n\t\treturn s.generateQuestionsWithContext(ctx, chatModel, truncate(content, half), \"\", \"\", docName, questionCount, custom)\n\tcase isTransientLLMError(err): // timeout, 5xx, rate limit\n\t\treturn nil, fmt.Errorf(\"failed to generate questions (retryable): %w\", err)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"failed to generate questions: %w\", err)\n\t}\n}","preventionTips":["Add bounded retry with exponential backoff for transient LLM errors instead of silently skipping chunks","Truncate or cap chunk + surrounding context to fit the model context window","Rotate/monitor provider API keys and set quota alerts","Track llm_call_failed metrics per provider to catch degradation early","Disable thinking and keep MaxTokens modest (as done here) to reduce provider rejections"],"tags":["llm","api-error","network","retry"],"backgroundTag":"llm-api-call-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}