spring-projects/spring-ai · error · java.lang.IllegalArgumentException

At least one client Id must be specified

Error message

At least one client Id must be specified

What it means

SyncProgressSpecification is a record pairing a set of client ids with a progress handler. Its compact constructor validates that the clients array is non-null, contains at least one entry, and has no blank (empty/whitespace-only) ids; otherwise it throws IllegalArgumentException. This guarantees a progress specification always targets at least one client.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/SyncProgressSpecification.java:37

import java.util.Arrays;
import java.util.Objects;
import java.util.function.Consumer;

import io.modelcontextprotocol.spec.McpSchema.ProgressNotification;

/**
 * Specification for synchronous progress handlers.
 *
 * @param clients The client IDs for the progress handler
 * @param progressHandler The consumer that handles progress notifications
 * @author Christian Tzolov
 */
public record SyncProgressSpecification(String[] clients, Consumer<ProgressNotification> progressHandler) {

	public SyncProgressSpecification {
		Objects.requireNonNull(clients, "clients must not be null");
		if (clients.length == 0 || Arrays.stream(clients).map(String::trim).anyMatch(String::isEmpty)) {
			throw new IllegalArgumentException("At least one client Id must be specified");
		}
		Objects.requireNonNull(progressHandler, "progressHandler must not be null");
	}

}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Pass at least one non-blank client id when creating the specification, e.g. new SyncProgressSpecification(new String[] {"client1"}, handler).
  2. Filter blank entries before constructing: Arrays.stream(raw).map(String::trim).filter(s -> !s.isEmpty()).toArray(String[]::new).
  3. Guard the source of the client array (config/property) so an empty value falls back to a sensible default client list.

Example fix

// before
new SyncProgressSpecification(new String[0], handler);
// after
new SyncProgressSpecification(new String[] { "my-client" }, handler);
Defensive patterns

Strategy: validation

Validate before calling

String[] clients = resolveClients();
boolean valid = clients != null
    && clients.length > 0
    && Arrays.stream(clients).map(String::trim).noneMatch(String::isEmpty);
if (!valid) throw new IllegalStateException("Provide at least one non-blank client id");
new SyncProgressSpecification(clients, Objects.requireNonNull(handler));

Type guard

boolean hasClients(String[] c) { return c != null && c.length > 0 && Arrays.stream(c).map(String::trim).anyMatch(s -> !s.isEmpty()); }

Try / catch

try { spec = new SyncProgressSpecification(clients, handler); } catch (IllegalArgumentException e) { log.error("Invalid progress spec: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Constructing SyncProgressSpecification with new String[0]; with an array containing "" or whitespace-only strings after trim; or (for the related requireNonNull) with a null clients array or null progressHandler.

Common situations: Programmatic configuration building the client list from an empty collection or optional config property that defaults to empty; string-splitting a config value producing empty tokens (e.g. "a,,b"); copy-pasting registration code and leaving the client array unfilled.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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