From c000d3b0e0764a717b294d3daa5559c73f62a53b Mon Sep 17 00:00:00 2001 From: ccsang Date: Sun, 15 Mar 2026 05:47:47 +0000 Subject: [PATCH 1/2] fix(provider): add None check for completion.choices to prevent TypeError Fixes #6252 When using certain providers (e.g., OpenRouter), the API may return completion.choices as None instead of an empty list. This causes TypeError: object of type 'NoneType' has no len() when calling len(completion.choices). This fix adds None checks before len() calls in three locations: - Streaming chunk handler (line 314) - _extract_reasoning_content method (line 348) - _parse_openai_completion method (line 471) --- astrbot/core/provider/sources/openai_source.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index c40234ed47..fa422aafef 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -311,7 +311,7 @@ async def _query_stream( state.handle_chunk(chunk) except Exception as e: logger.warning("Saving chunk state error: " + str(e)) - if len(chunk.choices) == 0: + if chunk.choices is None or len(chunk.choices) == 0: continue delta = chunk.choices[0].delta # logger.debug(f"chunk delta: {delta}") @@ -345,7 +345,7 @@ def _extract_reasoning_content( ) -> str: """Extract reasoning content from OpenAI ChatCompletion if available.""" reasoning_text = "" - if len(completion.choices) == 0: + if completion.choices is None or len(completion.choices) == 0: return reasoning_text if isinstance(completion, ChatCompletion): choice = completion.choices[0] @@ -468,7 +468,7 @@ async def _parse_openai_completion( """Parse OpenAI ChatCompletion into LLMResponse""" llm_response = LLMResponse("assistant") - if len(completion.choices) == 0: + if completion.choices is None or len(completion.choices) == 0: raise Exception("API 返回的 completion 为空。") choice = completion.choices[0] From 88be9b0d2b78fc1bf22c150a027b3d28c5f8fe37 Mon Sep 17 00:00:00 2001 From: ccsang Date: Sun, 15 Mar 2026 07:35:28 +0000 Subject: [PATCH 2/2] refactor: simplify None check using Python truthiness Address Sourcery AI review comment: - Use 'if not completion.choices:' instead of explicit None check - This handles both None and empty list cases concisely - More Pythonic and easier to maintain --- astrbot/core/provider/sources/openai_source.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index fa422aafef..5b0e94e4b1 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -311,7 +311,7 @@ async def _query_stream( state.handle_chunk(chunk) except Exception as e: logger.warning("Saving chunk state error: " + str(e)) - if chunk.choices is None or len(chunk.choices) == 0: + if not chunk.choices: continue delta = chunk.choices[0].delta # logger.debug(f"chunk delta: {delta}") @@ -345,7 +345,7 @@ def _extract_reasoning_content( ) -> str: """Extract reasoning content from OpenAI ChatCompletion if available.""" reasoning_text = "" - if completion.choices is None or len(completion.choices) == 0: + if not completion.choices: return reasoning_text if isinstance(completion, ChatCompletion): choice = completion.choices[0] @@ -468,7 +468,7 @@ async def _parse_openai_completion( """Parse OpenAI ChatCompletion into LLMResponse""" llm_response = LLMResponse("assistant") - if completion.choices is None or len(completion.choices) == 0: + if not completion.choices: raise Exception("API 返回的 completion 为空。") choice = completion.choices[0]