{"record":{"id":"9d0c567172f8867a","repo":"iflytek/astron-agent","slug":"spark-request-error-websocket-connection-closed","errorCode":"SPARK_REQUEST_ERROR","errorMessage":"WebSocket connection closed","messagePattern":"WebSocket connection closed","errorType":"error_code","errorClass":"CustomException","httpStatus":null,"severity":"critical","filePath":"core/workflow/infra/providers/llm/iflytek_spark/spark_fc_llm.py","lineNumber":137,"sourceCode":"                        cause_error=\"Sensitive content detected, LLM did not find function_call field\",\n                    )\n                if status != 2:\n                    continue\n                token_usage = msg[\"payload\"][\"usage\"][\"text\"]\n                if \"function_call\" not in msg[\"payload\"][\"choices\"][\"text\"][0]:\n                    raise CustomException(\n                        err_code=CodeEnum.SPARK_FUNCTION_NOT_CHOICE_ERROR,\n                        err_msg=\"Cannot find function_call field in LLM response\",\n                        cause_error=\"Cannot find function_call field in LLM response\",\n                    )\n\n                name = msg[\"payload\"][\"choices\"][\"text\"][0][\"function_call\"][\"name\"]\n                arguments = msg[\"payload\"][\"choices\"][\"text\"][0][\"function_call\"][\n                    \"arguments\"\n                ]\n                return name, token_usage, arguments\n            except websockets.ConnectionClosed:\n                raise CustomException(\n                    err_code=CodeEnum.SPARK_REQUEST_ERROR,\n                    err_msg=\"WebSocket connection closed\",\n                    cause_error=\"WebSocket connection closed\",\n                )\n            except Exception as e:\n                raise CustomException(\n                    err_code=CodeEnum.SPARK_REQUEST_ERROR,\n                    err_msg=f\"{e}\",\n                    cause_error=f\"{e}\",\n                )\n\n    async def _process_message(\n        self, msg: dict, span: Span\n    ) -> tuple[str | None, dict | None, str | None]:\n        \"\"\"\n        Process a single function call message from the API response.\n\n        :param msg: Message dictionary from Spark API","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/workflow/infra/providers/llm/iflytek_spark/spark_fc_llm.py#L119-L155","documentation":"SPARK_REQUEST_ERROR raised inside _recv_messages when websockets.ConnectionClosed escapes the recv loop — the Spark WebSocket dropped before a final (status==2) frame with the function_call was received. The client never got a complete answer, so no function name/arguments can be returned.","triggerScenarios":"During await ws_handle.recv() in the receive loop, the WebSocket protocol transitions to CLOSED (server closed the connection, network drop, or idle-connection teardown) before the function-call result frame arrives.","commonSituations":"Spark gateway closing long-idle connections; corporate proxy/firewall killing WebSockets; server restart or load-balancer timeout mid-request; unstable network between service and Spark endpoint.","solutions":["Retry async_call_spark_fc once or twice with backoff — a dropped connection is often transient","Inspect the span's error events and Spark response frames logged before the close for a server-side close code/reason","Verify network path (proxy, firewall, LB idle timeout) allows long-lived outbound WSS connections","Check Spark service status / your appid quota and concurrency limits that may cause the server to drop connections"],"exampleFix":"// before\nname, usage, args = await spark_fc.async_call_spark_fc(user_input, span)\n// after\nfor attempt in range(3):\n    try:\n        name, usage, args = await spark_fc.async_call_spark_fc(user_input, span)\n        break\n    except CustomException as e:\n        if 'WebSocket connection closed' in str(e.err_msg) and attempt < 2:\n            await asyncio.sleep(2 ** attempt)\n            continue\n        raise","handlingStrategy":"retry","validationCode":"# preflight: check endpoint reachability\nimport socket\nhost = urlparse(model_url).hostname\nsocket.getaddrinfo(host, 443)  # raises early on DNS/network issues","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        return await spark_fc.async_call_spark_fc(user_input, span)\n    except CustomException as e:\n        if 'WebSocket connection closed' in str(e.err_msg) and attempt < 2:\n            await asyncio.sleep(2 ** attempt)\n            continue\n        raise","preventionTips":["Implement automatic retry with exponential backoff on transient WS drops","Ensure proxies/LB idle timeouts exceed the expected request duration","Monitor Spark endpoint availability and alert on elevated drop rates","Keep requests short; chunk very long conversations"],"tags":["websocket","network","spark","connection-closed"],"backgroundTag":"connection-refused","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"}