spring-projects/spring-ai · warning

No choices returned for prompt: + prompt

Error message

No choices returned for prompt: + prompt

What it means

Warning logged by DeepSeekChatModel.internalCall() when the ChatCompletion body exists but its choices list is null. Like the null-body case, it returns an empty ChatResponse, so the caller gets a structurally valid response with zero generations instead of an exception.

Source

Thrown at models/spring-ai-deepseek/src/main/java/org/springframework/ai/deepseek/DeepSeekChatModel.java:165

					this.observationRegistry)
			.observe(() -> {

				ResponseEntity<ChatCompletion> completionEntity = RetryUtils.execute(this.retryTemplate,
						() -> this.deepSeekApi.chatCompletionEntity(request));

				var chatCompletion = completionEntity.getBody();

				if (chatCompletion == null) {
					if (logger.isWarnEnabled()) {
						logger.warn("No chat completion returned for prompt: " + prompt);
					}
					return new ChatResponse(List.of());
				}

				List<Choice> choices = chatCompletion.choices();
				if (choices == null) {
					if (logger.isWarnEnabled()) {
						logger.warn("No choices returned for prompt: " + prompt);
					}
					return new ChatResponse(List.of());
				}

				List<Generation> generations = choices.stream().map(choice -> {
			// @formatter:off
					Map<String, Object> metadata = Map.of(
							"id", chatCompletion.id() != null ? chatCompletion.id() : "",
							"role", choice.message().role() != null ? choice.message().role().name() : "",
							"index", choice.index(),
							"finishReason", choice.finishReason() != null ? choice.finishReason().name() : "");
					// @formatter:on
					return buildGeneration(choice, metadata);
				}).toList();

				// Current usage
				ChatCompletion body = completionEntity.getBody();
				Assert.state(body != null, "Body must not be null");

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Log/inspect the full ChatCompletion body (including any error or usage fields) to see why choices are absent.
  2. Review the prompt for content that DeepSeek may refuse and adjust it.
  3. Check deepseek API version / spring-ai-deepseek version compatibility after upgrades.
  4. Handle the empty-generations ChatResponse defensively in application code.

Example fix

// before
ChatResponse r = chatModel.call(new Prompt(msg));
return r.getResults().get(0); // IndexOutOfBounds when choices null
// after
return r.getResults().isEmpty() ? Optional.empty() : Optional.of(r.getResults().get(0));
Defensive patterns

Strategy: type-guard

Type guard

static Optional<Generation> firstGeneration(ChatResponse r) {
    return (r == null || r.getResults() == null) ? Optional.empty()
            : r.getResults().stream().findFirst();
}

Try / catch

Optional<Generation> gen = firstGeneration(chatModel.call(new Prompt(msg)));
Generation g = gen.orElseThrow(() -> new IllegalStateException("DeepSeek returned no choices for prompt"));

Prevention

When it happens

Trigger: DeepSeek returned a JSON body without a 'choices' field — e.g. an error-shaped payload deserialized into ChatCompletion, a content-filter/empty completion, or an API schema change.

Common situations: Prompts rejected by content filtering; max_tokens or stop conditions producing no choices; DeepSeek API contract changes after model/version upgrades; using an incorrect base-url pointing to a compatible-but-different API.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/05a80a44a3501bc4. Report an issue: GitHub.