{"record":{"id":"249d96bfb9fbdbc7","repo":"iflytek/astron-agent","slug":"spark-function-not-choice-error","errorCode":"SPARK_FUNCTION_NOT_CHOICE_ERROR","errorMessage":"Cannot find function_call field in LLM response","messagePattern":"Cannot find function_call field in LLM response","errorType":"error_code","errorClass":"CustomException","httpStatus":null,"severity":"error","filePath":"core/workflow/infra/providers/llm/iflytek_spark/spark_fc_llm.py","lineNumber":125,"sourceCode":"                if code != 0:\n                    raise CustomException(\n                        err_code=CodeConvert.sparkCode(code),\n                        cause_error=json.dumps(msg, ensure_ascii=False),\n                    )\n                status = msg[\"header\"][\"status\"]\n                llm_service_sid = msg[\"header\"][\"sid\"]\n                # Check if it's a quick repair: if the last character of sid is '1', it's a quick repair\n                if llm_service_sid[-1] == \"1\":\n                    raise CustomException(\n                        err_code=CodeEnum.SPARK_QUICK_REPAIR_ERROR,\n                        err_msg=\"Sensitive content detected, LLM did not find function_call field\",\n                        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(","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/workflow/infra/providers/llm/iflytek_spark/spark_fc_llm.py#L107-L143","documentation":"This CustomException (SPARK_FUNCTION_NOT_CHOICE_ERROR) is raised by the iFlytek Spark function-calling client when the final WebSocket frame (header.status == 2) arrives but the model's reply text entry lacks a 'function_call' key. It means Spark answered with plain content instead of selecting one of the supplied functions, so the caller cannot extract a function name/arguments.","triggerScenarios":"In _recv_messages/_process_message: header.code == 0, header.status == 2 (final frame), but 'function_call' not in msg['payload']['choices']['text'][0] — i.e. the model chose not to call any function or returned a normal text answer.","commonSituations":"User prompt doesn't match any registered function; functions array sent empty or malformed; temperature/top_k tuned so the model prefers free text; Spark content filtering stripped the function decision; prompt history nudges the model toward conversational replies.","solutions":["Check the frame payload in the tracing span (function_call_recv event) to confirm the final frame really has no function_call field","Ensure a non-empty, schema-valid functions list is passed to async_call_spark_fc and that descriptions clearly indicate when to call each function","Rephrase the user prompt so it clearly requests an action the functions can perform, or adjust temperature/top_k","If a text answer (no function call) is legitimate for your flow, handle the None/missing case instead of treating it as an error"],"exampleFix":"// before\nname, usage, arguments = await await_spark_fc(user_input, functions)\n// after\ntry:\n    name, usage, arguments = await await_spark_fc(user_input, functions)\nexcept CustomException as e:\n    if e.err_code == CodeEnum.SPARK_FUNCTION_NOT_CHOICE_ERROR:\n        fallback_answer = await plain_chat(user_input)\n    else:\n        raise","handlingStrategy":"fallback","validationCode":"def functions_ready(functions):\n    return bool(functions) and all(f.get('name') and f.get('description') for f in functions)\nif not functions_ready(function_list):\n    raise ValueError('functions list must be non-empty with name+description')","typeGuard":"def has_function_call(msg: dict) -> bool:\n    choices = msg.get('payload', {}).get('choices', {}).get('text', [])\n    return bool(choices) and 'function_call' in choices[0]","tryCatchPattern":"try:\n    name, usage, args = await spark_fc.async_call_spark_fc(user_input, span)\nexcept CustomException as e:\n    if e.err_code == CodeEnum.SPARK_FUNCTION_NOT_CHOICE_ERROR:\n        name, usage, args = None, None, None\n    else:\n        raise","preventionTips":["Always pass a non-empty, well-described functions list","Test prompts against the function set before shipping workflows","Decide explicitly how 'no function chosen' should behave and code that path","Monitor span traces for repeated no-function-call responses per prompt template"],"tags":["llm","function-calling","spark","api-response"],"backgroundTag":"unexpected-api-response-shape","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"}