spring-projects/spring-ai · warning

No choices returned for prompt: ${prompt}

Error message

No choices returned for prompt: ${prompt}

What it means

OpenAiChatModel.internalCall sends a chat completion request to the OpenAI API and checks the returned ChatCompletion. When the API responds successfully but the choices list is empty, Spring AI logs a warning and returns an empty ChatResponse instead of throwing, because there are no generations to build. This usually signals the provider accepted the request but produced no candidate completions.

Source

Thrown at models/spring-ai-openai/src/main/java/org/springframework/ai/openai/OpenAiChatModel.java:228

		ChatCompletionCreateParams request = this.createRequest(prompt, false);
		RequestOptions requestOptions = this.buildRequestOptions(prompt);

		ChatModelObservationContext observationContext = ChatModelObservationContext.builder()
			.prompt(prompt)
			.provider(AiProvider.OPENAI.value())
			.build();

		ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
			.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
					this.observationRegistry)
			.observe(() -> {

				ChatCompletion chatCompletion = this.openAiClient.chat().completions().create(request, requestOptions);

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

				List<Generation> generations = choices.stream().map(choice -> {
					Map<String, Object> metadata = Map.of("id", chatCompletion.id(), "role",
							choice.message()._role().asString().isPresent() ? choice.message()._role().asStringOrThrow()
									: "",
							"index", choice.index(), "finishReason", choice.finishReason().value().toString(),
							"refusal", choice.message().refusal().orElse(""), "annotations",
							choice.message().annotations().orElse((List) List.of(Map.of())), REASONING_CONTENT,
							getReasoningContent(choice));
					return buildGeneration(choice, metadata, request);
				}).toList();

				// Current usage
				CompletionUsage usage = chatCompletion.usage().orElse(null);
				Usage currentChatResponseUsage = usage != null ? getDefaultUsage(usage) : new EmptyUsage();

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Check the response object / enable DEBUG logging to see the raw API response and why choices were empty
  2. If using a proxy or compatible endpoint, verify it returns a standard choices array in the OpenAI chat completion schema
  3. Rephrase the prompt if content filtering may have suppressed the output
  4. Handle the empty ChatResponse in your code: check ChatResponse.getResults() is non-empty before reading generations

Example fix

// before
ChatResponse response = chatModel.call(new Prompt("..."));
String text = response.getResult().getOutput().getText(); // NPE if empty
// after
ChatResponse response = chatModel.call(new Prompt("..."));
if (response == null || response.getResults().isEmpty()) {
    throw new IllegalStateException("Model returned no completions");
}
String text = response.getResult().getOutput().getText();
Defensive patterns

Strategy: validation

Validate before calling

// after the call
ChatResponse resp = chatModel.call(prompt);
if (resp == null || resp.getResults() == null || resp.getResults().isEmpty()) {
    throw new IllegalStateException("OpenAI returned no choices for prompt");
}

Prevention

When it happens

Trigger: Calling OpenAiChatModel.call(prompt) where the OpenAI API returns a ChatCompletion whose choices() list is empty — e.g. the request was filtered by content policy, the API returned a degenerate/empty response, or an unexpected response shape from a non-standard endpoint.

Common situations: Pointing spring.ai.openai.base-url at a non-OpenAI OpenAI-compatible proxy that omits the choices array; safety filtering of flagged prompts; API version changes that change response shape.

Understand the failure class

Background: "empty response", "returned no data", "empty embeddings": what HTTP 200-with-empty-body errors mean across libraries — this error's family across 36 libraries.

Related errors


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