spring-projects/spring-ai · warning
Query compression result is null/empty. Returning the input…
Error message
Query compression result is null/empty. Returning the input query unchanged.
What it means
CompressionQueryTransformer.transform asks the chat model to compress a conversation history plus query into a standalone query; when the returned content is null or blank it logs this warning and returns the input query unchanged. The pipeline continues with the un-compressed query instead of failing.
Solutions
- Increase maxTokens and verify the compression prompt template renders the history correctly.
- Log the raw ChatResponse finish reason to see why content is empty.
- Accept the fallback path — ensure callers can work with the original multi-turn query.
- Switch to a model that reliably returns text for compression tasks.
Example fix
// before
CompressionQueryTransformer transformer = CompressionQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate())
.build();
// after — give the model room to answer
CompressionQueryTransformer transformer = CompressionQueryTransformer.builder()
.chatClientBuilder(chatClient.mutate()
.defaultOptions(OpenAiChatOptions.builder().maxTokens(512).build()))
.build(); Defensive patterns
Strategy: fallback
Validate before calling
// ensure the history isn't truncated to nothing
if (query.history() == null || query.history().isEmpty()) {
logger.debug("No history to compress; transformer will pass query through");
} Try / catch
Query transformed = transformer.transform(query);
if (transformed.text().equals(query.text()) && query.history() != null && !query.history().isEmpty()) {
logger.info("Compression fell back to original query; check model output");
} Prevention
- Raise maxTokens for the compression model.
- Log ChatResponse finish reasons when content is empty.
- Use an instruct (non-reasoning) model for compression.
- Only apply the transformer when a meaningful history exists.
When it happens
Trigger: transform() calls the compression prompt with history/query params and .call().content() returns null or whitespace (empty completion, content filter, or non-text response).
Common situations: Long histories with a low maxTokens budget yielding empty output; safety filters blocking the compressed output; misconfigured model; reasoning model returning no plain content.
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
- Query expansion result is null. Returning the input query…
- Query rewrite result is null/empty. Returning the input…
- Could not parse text search score
- Database product name is null or empty, defaulting to…
- Due to failure in establishing JDBC connection or parsing…
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/8e7d535772cbb711.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-rag/src/main/java/org/springframework/ai/rag/preretrieval/query/transformation/CompressionQueryTransformer.java:90
PromptAssert.templateHasRequiredPlaceholders(this.promptTemplate, "history", "query");
}
@Override
public Query transform(Query query) {
Assert.notNull(query, "query cannot be null");
logger.debug("Compressing conversation history and follow-up query into a standalone query");
var compressedQueryText = this.chatClient.prompt()
.user(user -> user.text(this.promptTemplate.getTemplate())
.param("history", formatConversationHistory(query.history()))
.param("query", query.text()))
.call()
.content();
if (!StringUtils.hasText(compressedQueryText)) {
logger.warn("Query compression result is null/empty. Returning the input query unchanged.");
return query;
}
return query.mutate().text(compressedQueryText).build();
}
private String formatConversationHistory(List<Message> history) {
if (history.isEmpty()) {
return "";
}
return history.stream()
.filter(message -> message.getMessageType().equals(MessageType.USER)
|| message.getMessageType().equals(MessageType.ASSISTANT))
.map(message -> "%s: %s".formatted(message.getMessageType(), message.getText()))
.collect(Collectors.joining("\n"));
}
View on GitHub (pinned to 98a7beda4f)