Missing array bounds checking in LLM provider response handling #8272

Open
opened 2026-02-16 18:09:34 -05:00 by yindo · 1 comment
Owner

Originally created by @riftzen-bit on GitHub (Feb 1, 2026).

Originally assigned to: @rekram1-node on GitHub.

Summary

The LLM provider SDK code accesses array elements without checking if the array is non-empty, which can cause runtime crashes when the API returns unexpected responses.

Locations

  • packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts:216
  • packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts:435
  • packages/opencode/src/provider/sdk/copilot/responses/openai-responses-language-model.ts:435

Issue

// Line 216 - doGenerate()
const choice = responseBody.choices[0]  // No check if choices array is empty
const content: Array<LanguageModelV2Content> = []
const text = choice.message.content  // Will throw if choice is undefined

// Line 435 - doStream()
const choice = value.choices[0]  // Same issue in streaming
if (choice?.finish_reason != null) {
  // ...
}

If the API returns an empty choices array (which can happen on errors or edge cases), accessing choices[0] returns undefined, and subsequent property access throws "Cannot read property 'message' of undefined".

Impact

  • Severity: Medium
  • Type: Runtime Error
  • Effect: Unhandled exception when API returns empty choices

Suggested Fix

Add bounds checking:

const choice = responseBody.choices[0]
if (!choice) {
  throw new InvalidResponseDataError({
    data: responseBody,
    message: "Response contained no choices"
  })
}

Or use optional chaining with fallback:

const choice = responseBody.choices?.[0]
if (!choice?.message) {
  // Handle empty response case
}
Originally created by @riftzen-bit on GitHub (Feb 1, 2026). Originally assigned to: @rekram1-node on GitHub. ## Summary The LLM provider SDK code accesses array elements without checking if the array is non-empty, which can cause runtime crashes when the API returns unexpected responses. ## Locations - `packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts:216` - `packages/opencode/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts:435` - `packages/opencode/src/provider/sdk/copilot/responses/openai-responses-language-model.ts:435` ## Issue ```typescript // Line 216 - doGenerate() const choice = responseBody.choices[0] // No check if choices array is empty const content: Array<LanguageModelV2Content> = [] const text = choice.message.content // Will throw if choice is undefined // Line 435 - doStream() const choice = value.choices[0] // Same issue in streaming if (choice?.finish_reason != null) { // ... } ``` If the API returns an empty `choices` array (which can happen on errors or edge cases), accessing `choices[0]` returns `undefined`, and subsequent property access throws "Cannot read property 'message' of undefined". ## Impact - **Severity**: Medium - **Type**: Runtime Error - **Effect**: Unhandled exception when API returns empty choices ## Suggested Fix Add bounds checking: ```typescript const choice = responseBody.choices[0] if (!choice) { throw new InvalidResponseDataError({ data: responseBody, message: "Response contained no choices" }) } ``` Or use optional chaining with fallback: ```typescript const choice = responseBody.choices?.[0] if (!choice?.message) { // Handle empty response case } ```
Author
Owner

@mugnimaestra commented on GitHub (Feb 7, 2026):

Tangentially related: PR #12567 includes a fix in the same file (openai-compatible-chat-language-model.ts) for a different error path — the Copilot API can send multiple reasoning_opaque deltas in a single response (e.g., one with reasoning text, another with tool calls), and the previous code threw InvalidResponseDataError on the second one. The fix now accepts the latest value instead. Not a bounds-check fix, but addresses another crash scenario in this provider.

@mugnimaestra commented on GitHub (Feb 7, 2026): Tangentially related: PR #12567 includes a fix in the same file (`openai-compatible-chat-language-model.ts`) for a different error path — the Copilot API can send multiple `reasoning_opaque` deltas in a single response (e.g., one with reasoning text, another with tool calls), and the previous code threw `InvalidResponseDataError` on the second one. The fix now accepts the latest value instead. Not a bounds-check fix, but addresses another crash scenario in this provider.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#8272