{"record":{"id":"108a6ad6905d31ee","repo":"iflytek/astron-agent","slug":"spark-request-error","errorCode":"SPARK_REQUEST_ERROR","errorMessage":"LLM returned empty result","messagePattern":"LLM returned empty result","errorType":"error_code","errorClass":"CustomException","httpStatus":null,"severity":"error","filePath":"core/workflow/engine/nodes/base_node.py","lineNumber":1334,"sourceCode":"                await self.put_llm_content(\n                    node_id=self.node_id,\n                    model_name=self.domain,\n                    variable_pool=request.variable_pool,\n                    msg_or_end_node_deps=request.msg_or_end_node_deps,\n                    llm_content=msg,\n                )\n            texts.append(content or \"\")\n            if status in {\n                SparkLLMStatus.END.value,\n                ChatStatus.FINISH_REASON.value,\n            }:\n                break\n            if self._is_unexpected_finish_status(status):\n                raise CustomException(err_code=CodeEnum.OPEN_AI_REQUEST_ERROR)\n\n        if not texts:\n            request.span.add_error_event(\"result is null\")\n            raise CustomException(\n                err_code=CodeEnum.SPARK_REQUEST_ERROR,\n                err_msg=\"LLM returned empty result\",\n                cause_error=\"LLM returned empty result\",\n            )\n        return token_usage, \"\".join(texts), \"\".join(reasoning_contents), status\n\n    async def _finish_generation_span(\n        self,\n        span: Span,\n        answer: str,\n        reasoning: str,\n        token_usage: dict,\n        status: Any,\n    ) -> None:\n        await span.add_info_events_async({\"spark_llm_chat_result\": answer})\n        await span.add_info_events_async({\"spark_llm_reasoning_content\": reasoning})\n        result_attributes = langfuse_observation_attributes(\n            \"generation\",","sourceCodeStart":1316,"sourceCodeEnd":1352,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/workflow/engine/nodes/base_node.py#L1316-L1352","documentation":"Thrown by the workflow engine's LLM streaming consumer (_consume_llm_stream, called from _chat_with_llm) when the model stream ends without yielding any text chunks. The engine treats an empty completion as a provider/model failure rather than a valid empty answer, tags the span with 'result is null', and raises SPARK_REQUEST_ERROR so the node fails visibly instead of returning an empty string downstream.","triggerScenarios":"The LLM stream finishes (normal or unexpected stop status) while the accumulated `texts` list is empty — e.g. the model returned only reasoning content, the provider sent zero content deltas, a content filter stripped the output, or the stream was cut before any chunk arrived.","commonSituations":"Misconfigured model endpoint returning 200 with an empty body; max_tokens set too low so the model emits nothing; prompt triggers a safety refusal that suppresses content; reasoning-only models whose text field is empty; transient provider outages; wrong API key causing a silently empty stream wrapper.","solutions":["Check the span error event 'result is null' and the upstream provider logs to confirm whether the stream contained any chunks at all.","Retry the request; if intermittent, add retry with backoff around the LLM call in _chat_with_llm.","Verify model configuration: max_tokens, temperature, and that the selected model actually produces content (not reasoning-only) for this prompt.","Validate the API key/endpoint and that content filters are not suppressing output; test the same prompt against the provider directly.","If the model legitimately returns empty output, handle it upstream (e.g. add a fallback prompt or allow empty results) before it reaches this check."],"exampleFix":"// before: empty stream bubbles up as SPARK_REQUEST_ERROR\nresult = await node._chat_with_llm(prompt)\n\n// after: validate/retry before consuming\nfor attempt in range(3):\n    try:\n        result = await node._chat_with_llm(prompt)\n        if result.strip():\n            break\n    except CustomException:\n        if attempt == 2: raise\n        await asyncio.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"def has_text(stream_result):\n    return bool(stream_result and stream_result.strip())","typeGuard":"def is_nonempty_str(v) -> bool:\n    return isinstance(v, str) and len(v.strip()) > 0","tryCatchPattern":"try:\n    token_usage, text, reasoning, status = await node._chat_with_llm(prompt)\nexcept CustomException as e:\n    if e.err_code == CodeEnum.SPARK_REQUEST_ERROR:\n        log.warning(\"empty LLM result, retrying\")\n        text = await retry_with_backoff(lambda: node._chat_with_llm(prompt))\n    else:\n        raise","preventionTips":["Set max_tokens high enough that the model always emits content","Test prompts against the provider directly to detect refilters/empty outputs","Add retry-with-backoff around LLM calls for transient provider failures","Monitor spans for 'result is null' events to catch systematic empty responses"],"tags":["llm","streaming","empty-response","workflow"],"backgroundTag":"empty-api-response","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}