spring-projects/spring-ai · warning · UnsupportedOperationException

This is a utility class and cannot be instantiated

Error message

This is a utility class and cannot be instantiated

What it means

UsageCalculator is a final utility class whose private constructor deliberately throws UnsupportedOperationException("This is a utility class and cannot be instantiated"). It only offers static methods (e.g. accumulateUsage), so instantiation is a programming mistake. The library throws this to enforce the utility-class pattern.

Source

Thrown at spring-ai-model/src/main/java/org/springframework/ai/support/UsageCalculator.java:35

package org.springframework.ai.support;

import org.jspecify.annotations.Nullable;

import org.springframework.ai.chat.metadata.ChatResponseMetadata;
import org.springframework.ai.chat.metadata.DefaultUsage;
import org.springframework.ai.chat.metadata.Usage;
import org.springframework.ai.chat.model.ChatResponse;

/**
 * A utility class to provide support methods handling {@link Usage}.
 *
 * @author Ilayaperumal Gopinathan
 * @author Jewoo Shin
 */
public final class UsageCalculator {

	private UsageCalculator() {
		throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
	}

	/**
	 * Accumulate usage tokens from the previous chat response to the current usage
	 * tokens.
	 * <p>
	 * Note: when the two usages are actually summed, the result is a plain
	 * {@link DefaultUsage} and the provider-specific {@link Usage#getNativeUsage() native
	 * usage} object is <em>not</em> preserved (it cannot be merged across responses).
	 * Only the token counts and cache metrics carry over. The original
	 * {@code currentUsage} (native usage included) is returned unchanged when there is
	 * nothing to accumulate.
	 * @param currentUsage the current usage.
	 * @param previousChatResponse the previous chat response.
	 * @return accumulated usage.
	 */
	public static Usage getCumulativeUsage(final Usage currentUsage,
			final @Nullable ChatResponse previousChatResponse) {

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Use the static methods directly, e.g. UsageCalculator.getUsage(previousResponse, currentResponse)
  2. Remove any @Bean/@Component registration or 'new UsageCalculator()' usage
  3. If wiring is needed, wrap it in your own service class instead of instantiating it

Example fix

// before
UsageCalculator calc = new UsageCalculator();
Usage usage = calc.getUsage(prev, curr);
// after
Usage usage = UsageCalculator.getUsage(prev, curr);
Defensive patterns

Strategy: type-guard

Validate before calling

// Never instantiate; detect accidental reflective instantiation early
if (UsageCalculator.class.getDeclaredConstructors().length > 0) { /* utility class, use statics */ }

Type guard

boolean isUtilityClassMisuse = false;
try { UsageCalculator.class.getDeclaredConstructor().newInstance(); isUtilityClassMisuse = true; }
catch (ReflectiveOperationException ignored) { }
// isUtilityClassMisuse == true means someone tried to instantiate it

Try / catch

try {
    Usage usage = UsageCalculator.getUsage(prev, curr);
} catch (UnsupportedOperationException e) {
    // you instantiated the utility class somewhere; fix the call site
    throw new AssertionError("Use UsageCalculator static methods", e);
}

Prevention

When it happens

Trigger: Calling new UsageCalculator() directly, or invoking reflection/bean-wiring that attempts to instantiate the class instead of using its static methods.

Common situations: Registering UsageCalculator as a Spring bean by mistake, copying code that instantiates other helper classes, or IDE auto-import followed by 'new UsageCalculator()'.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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