Skip to content

fix: guard against empty choices and message=None in OpenAI query#3

Open
qizwiz wants to merge 1 commit into
ARCANGEL0:mainfrom
qizwiz:fix/guard-empty-llm-choices
Open

fix: guard against empty choices and message=None in OpenAI query#3
qizwiz wants to merge 1 commit into
ARCANGEL0:mainfrom
qizwiz:fix/guard-empty-llm-choices

Conversation

@qizwiz

@qizwiz qizwiz commented May 18, 2026

Copy link
Copy Markdown

What

Add explicit guards in _query_openai() before accessing choices[0].message.content on both the primary and fallback model paths.

Why

openai.chat.completions.create() can return two distinct empty-response shapes:

  1. choices = [] — on content-policy rejections, rate-limit errors, or provider failures
  2. choices[0].message = None — Gemini 2.5 Flash (via the OpenAI-compatible endpoint) returns HTTP 200 with finish_reason: PROHIBITED_CONTENT and message=None rather than raising an exception

Both currently crash with IndexError or AttributeError. The outer except Exception catches these crashes silently, masking structural response issues as provider errors and incorrectly triggering the fallback model.

With the guards, a ValueError is raised explicitly so the fallback is only triggered by genuine provider-level failures, not malformed responses.

Change

# Before — crashes on empty choices or message=None
return completion.choices[0].message.content

# After — explicit guard
if not completion.choices or completion.choices[0].message is None:
    raise ValueError("LLM returned empty or filtered response")
return completion.choices[0].message.content

Applied to both the primary (gpt-5) and fallback (gpt-4.1) paths.

Corpus context

Detected by pact (llm_response_unguarded mode), a Z3-verified static analyzer for LLM crash vectors. This pattern was found in 13.5k+ violations across 761 repos.

openai.chat.completions.create() can return choices=[] (network/content
errors) or choices[0].message=None (e.g. Gemini PROHIBITED_CONTENT via
OpenAI-compatible endpoint). Both paths crash silently inside the outer
try/except; explicit guards raise ValueError so the fallback model is
tried only for provider errors, not for structural response issues.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant