iflytek/astron-agent · error · CustomException
QUESTION_ANSWER_HANDLER_RESPONSE_ERROR
QUESTION_ANSWER_HANDLER_RESPONSE_ERROR
Error message
Parse result: {model_res} is abnormal What it means
In the prompt-template mode of the question-answer node, the LLM response is expected to be a JSON object (dict) after `json.loads`. If parsing succeeds but yields a non-dict (e.g. a JSON list, string, or number), the node raises QUESTION_ANSWER_HANDLER_RESPONSE_ERROR stating the parsed result is abnormal.
Solutions
- Strengthen the prompt template to explicitly demand a single JSON object, e.g. 'Respond with exactly one JSON object of the form {...}', and include a concrete example.
- Validate/normalize the LLM output before parsing: if loads() yields a list, wrap or map it into the expected dict shape.
- Enable/adjust JSON mode or a structured-output constraint on the model call so the top level is guaranteed to be an object.
- Add retries: catch this error in handle_prompt_template_response's retry loop and re-prompt the model (the node already supports max_retries for slot extraction).
Example fix
# before
model_res = json.loads(json_str)
if not isinstance(model_res, dict):
raise CustomException(...)
# after
model_res = json.loads(json_str)
if isinstance(model_res, list) and len(model_res) == 1:
model_res = model_res[0]
if not isinstance(model_res, dict):
raise CustomException(...) Defensive patterns
Strategy: type-guard
Validate before calling
def parsed_is_dict(raw: str) -> bool:
try:
return isinstance(json.loads(raw), dict)
except Exception:
return False Type guard
def is_dict(obj) -> bool:
return isinstance(obj, dict) Try / catch
try:
result = await run_question_answer_node(...)
except CustomException as e:
if e.err_code == CodeEnum.QUESTION_ANSWER_HANDLER_RESPONSE_ERROR:
logger.warning(f"non-dict model output, retrying: {e.err_msg}")
result = await retry_with_stricter_prompt(...)
else:
raise Prevention
- Include an exact JSON-object example in the extraction prompt.
- Prefer the model's native JSON/structured-output mode.
- Keep temperature low for extraction tasks.
- Always assert the parsed type immediately after json.loads.
When it happens
Trigger: async_execute_prompt -> json.loads(json_str) returns a top-level non-dict value: the LLM produced a JSON array (e.g. `["A","B"]`), a bare string, number, or `null` instead of the expected `{...}` option/parameter object.
Common situations: Prompt template not strict enough about output shape so the model emits a JSON array of options; model wrapped output so the extracted json_str is just a quoted scalar; temperature/top_p changes make output shape unstable across model versions; switching to a model that ignores the JSON-object instruction.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/6c8e6e9a3e7a64f5.
Report an issue: GitHub.
Appendix: source
Thrown at core/workflow/engine/nodes/question_answer/question_answer_node.py:562
event_log_node_trace=event_log_node_trace,
)
self.calculate_usage_token(token_usage)
await span_context.add_info_events_async(
{"token_usage": json.dumps(self.token_usage, ensure_ascii=False)}
)
# 5. Extract JSON block
json_match = re.search(r"```json\s*\n?(.*?)\n?```", response, re.DOTALL)
json_str = json_match.group(1).strip() if json_match else response.strip()
await span_context.add_info_events_async(
{"llm_result": response, "json_str": json_str}
)
# 6. Safely parse JSON
try:
model_res = json.loads(json_str)
if not isinstance(model_res, dict):
err_msg = f"Parse result: {model_res} is abnormal"
raise CustomException(
err_code=CodeEnum.QUESTION_ANSWER_HANDLER_RESPONSE_ERROR,
err_msg=err_msg,
cause_error=err_msg,
)
# Record parsed content
await span_context.add_info_events_async(
{
"extracted_params": json.dumps(model_res, ensure_ascii=False),
"token_usage": str(token_usage),
}
)
# 7. Build return object
prompt_result = PromptResult(
role=model_res.get("role", "assistant"),
content=model_res.get("content", ""),
complete_data=model_res.get("completed", {}),View on GitHub (pinned to 5e758547a8)