alibaba/spring-ai-alibaba · warning

Cannot find safe cutoff point for summarization

Error message

Cannot find safe cutoff point for summarization

What it means

SummarizationHook.beforeModel triggers summarization when token count exceeds maxTokensBeforeSummary, then calls findSafeCutoff() to find an index where the message list can be split (e.g. a boundary that keeps tool-call/tool-result pairs intact). If cutoffIndex <= 0 no safe boundary exists, so it logs this warning and returns the previous messages unchanged — summarization is skipped and the oversized context is passed to the model as-is.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/summarization/SummarizationHook.java:120

	@Override
	public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
		if (maxTokensBeforeSummary == null) {
			return new AgentCommand(previousMessages);
		}

		int totalTokens = tokenCounter.countTokens(previousMessages);

		if (totalTokens < maxTokensBeforeSummary) {
			return new AgentCommand(previousMessages);
		}

		log.info("Token count {} exceeds threshold {}, triggering summarization",
				totalTokens, maxTokensBeforeSummary);

		int cutoffIndex = findSafeCutoff(previousMessages);

		if (cutoffIndex <= 0) {
			log.warn("Cannot find safe cutoff point for summarization");
			return new AgentCommand(previousMessages);
		}

		UserMessage firstUserMessage = null;
		if (keepFirstUserMessage) {
			for (Message msg : previousMessages) {
				if (msg instanceof UserMessage) {
					firstUserMessage = (UserMessage) msg;
					break;
				}
			}
		}

		List<Message> toSummarize = new ArrayList<>();
		for (int i = 0; i < cutoffIndex; i++) {
			Message msg = previousMessages.get(i);
			if (msg != firstUserMessage) {
				toSummarize.add(msg);

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Raise maxTokensBeforeSummary so summarization triggers only when there is meaningful earlier history to cut
  2. Ensure tool results are truncated/compacted before entering state so findSafeCutoff can locate a boundary
  3. Increase the minimum retained messages window so the cutoff search has candidates
  4. As a guard, also cap total input tokens at the model level (context compaction / windowing) since this warning means no summarization happened
  5. Inspect findSafeCutoff rules and adjust the boundary policy if your message patterns never satisfy it

Example fix

// before
SummarizationHook hook = SummarizationHook.builder()
    .maxTokensBeforeSummary(2000) // too low: cutoff search fails immediately
    .build();
// after
SummarizationHook hook = SummarizationHook.builder()
    .maxTokensBeforeSummary(20000)
    .keepFirstUserMessage(true)
    .build();
Defensive patterns

Strategy: fallback

Validate before calling

int approxTokens = messages.stream().mapToInt(m -> m.getText().length() / 4).sum();
boolean summarizable = approxTokens > maxTokensBeforeSummary && approxTokens < hardModelLimit;

Try / catch

AgentCommand cmd = hook.beforeModel(state, config).join();
if (cmd.getMessages().size() >= state.messages().size()) { /* summarization skipped; apply manual compaction */ }

Prevention

When it happens

Trigger: History exceeds the token threshold but is composed such that no safe cutoff exists — e.g. the conversation is almost entirely one giant tool-call/tool-result sequence, or messages start mid tool-interaction so cutting anywhere would orphan a tool result.

Common situations: A single very long tool response dominating the history; maxTokensBeforeSummary set so low that the first turn already exceeds it; aggressive tool output (web pages, file dumps) early in conversation; keepFirstUserMessage logic combined with a tiny history.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/80b90cb08a7f94e2. Report an issue: GitHub.