langchain4j/langchain4j-mistral-ai
Bowang 9ec303daa6
fix(mistralai): guard against empty/null choices in MistralAiChatModel.doChat (#5821)
## Issue

`MistralAiChatModel.doChat()` accesses
`mistralAiResponse.getChoices().get(0)` without checking whether
`choices` is null or empty. When the Mistral AI API (or an
OpenAI-compatible server fronted by it, e.g. vLLM/llama.cpp/Ollama in
OpenAI mode) returns a response with no choices — content filtering,
quota or rate-limit errors, malformed responses — this throws a cryptic
`IndexOutOfBoundsException` (empty list) or `NullPointerException`
(null) instead of a clear failure.

This is the same class of issue reported for `OpenAiChatModel` in #4810,
and complements the `content: null` null-guard recently added to
`MistralAiMapper.aiMessageFrom` in #5123. That guard covers *non-empty*
choices whose `message.content` is null; this PR covers the orthogonal
case of *empty/null* choices themselves. No existing issue covers the
empty-choices case for the MistralAI integration.

## Change

`MistralAiChatModel.doChat()` now checks
`isNullOrEmpty(mistralAiResponse.getChoices())` before the choices are
consumed and throws a descriptive `IllegalArgumentException`, mirroring
the guard already applied in `OpenAiChatModel` (see #4810):

```java
if (isNullOrEmpty(mistralAiResponse.getChoices())) {
    throw new IllegalArgumentException("Mistral AI response has no choices");
}
```

`isNullOrEmpty` is the same helper already imported across the codebase.
No public API or behaviour change for well-formed responses; only the
previously-crashing path now throws a clear exception instead.

## Tests

Added `MistralAiChatModelEmptyChoicesTest`, mirroring the existing
`MistralAiChatModelToolCallsTest` /
`MistralAiChatModelReturnThinkingTest` style (uses `MockHttpClient`, no
API key required). Covers:

1.
`should_throw_IllegalArgumentException_when_response_has_empty_choices`
— empty `choices: []` list → `IllegalArgumentException` (previously
`IndexOutOfBoundsException`).
2.
`should_throw_IllegalArgumentException_when_response_has_null_choices` —
`choices` absent (deserializes to `null`) → `IllegalArgumentException`
(previously `NullPointerException`).
3. `should_return_chat_response_when_response_has_choices` — regression
guard: a well-formed single-choice response is still parsed correctly.

## General checklist
- [X] There are no breaking changes (API, behaviour)
- [X] I have added unit and/or integration tests for my change
- [X] The tests cover both positive and negative cases
- [X] I have manually run all the unit and integration tests in the
module I have added/changed, and they are all green

`mvn -pl langchain4j-mistral-ai -am test
-Dtest=MistralAiChatModelEmptyChoicesTest
-Dsurefire.failIfNoSpecifiedTests=false` → `Tests run: 3, Failures: 0,
Errors: 0, Skipped: 0`, BUILD SUCCESS.
- [ ] I have manually run all the unit and integration tests in the
[core](https://github.com/langchain4j/langchain4j/tree/main/langchain4j-core)
and
[main](https://github.com/langchain4j/langchain4j/tree/main/langchain4j)
modules, and they are all green
- [ ] I have added/updated the
[documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs)
- [ ] I have added an example in the [examples
repo](https://github.com/langchain4j/langchain4j-examples) (only for
"big" features)
- [ ] I have added/updated [Spring Boot
starter(s)](https://github.com/langchain4j/langchain4j-spring) (if
applicable)
2026-07-31 15:49:11 +02:00
..
src fix(mistralai): guard against empty/null choices in MistralAiChatModel.doChat (#5821) 2026-07-31 15:49:11 +02:00
pom.xml Update versions to 1.19.0-SNAPSHOT and 1.19.0-beta29-SNAPSHOT 2026-07-17 13:42:56 +00:00
revapi.json Introduce `MistralAiChatRequestParameters` for per-request overrides (#5852) 2026-07-27 11:13:30 +02:00