{"record":{"id":"303202be4b0129c0","repo":"spring-projects/spring-ai","slug":"at-least-one-client-id-must-be-specified-303202","errorCode":null,"errorMessage":"At least one client Id must be specified","messagePattern":"At least one client Id must be specified","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/SyncProgressSpecification.java","lineNumber":37,"sourceCode":"import java.util.Arrays;\nimport java.util.Objects;\nimport java.util.function.Consumer;\n\nimport io.modelcontextprotocol.spec.McpSchema.ProgressNotification;\n\n/**\n * Specification for synchronous progress handlers.\n *\n * @param clients The client IDs for the progress handler\n * @param progressHandler The consumer that handles progress notifications\n * @author Christian Tzolov\n */\npublic record SyncProgressSpecification(String[] clients, Consumer<ProgressNotification> progressHandler) {\n\n\tpublic SyncProgressSpecification {\n\t\tObjects.requireNonNull(clients, \"clients must not be null\");\n\t\tif (clients.length == 0 || Arrays.stream(clients).map(String::trim).anyMatch(String::isEmpty)) {\n\t\t\tthrow new IllegalArgumentException(\"At least one client Id must be specified\");\n\t\t}\n\t\tObjects.requireNonNull(progressHandler, \"progressHandler must not be null\");\n\t}\n\n}\n","sourceCodeStart":19,"sourceCodeEnd":43,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/progress/SyncProgressSpecification.java#L19-L43","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass at least one non-blank client id when creating the specification, e.g. new SyncProgressSpecification(new String[] {\"client1\"}, handler).","Filter blank entries before constructing: Arrays.stream(raw).map(String::trim).filter(s -> !s.isEmpty()).toArray(String[]::new).","Guard the source of the client array (config/property) so an empty value falls back to a sensible default client list."],"exampleFix":"// before\nnew SyncProgressSpecification(new String[0], handler);\n// after\nnew SyncProgressSpecification(new String[] { \"my-client\" }, handler);","handlingStrategy":"validation","validationCode":"String[] clients = resolveClients();\nboolean valid = clients != null\n    && clients.length > 0\n    && Arrays.stream(clients).map(String::trim).noneMatch(String::isEmpty);\nif (!valid) throw new IllegalStateException(\"Provide at least one non-blank client id\");\nnew SyncProgressSpecification(clients, Objects.requireNonNull(handler));","typeGuard":"boolean hasClients(String[] c) { return c != null && c.length > 0 && Arrays.stream(c).map(String::trim).anyMatch(s -> !s.isEmpty()); }","tryCatchPattern":"try { spec = new SyncProgressSpecification(clients, handler); } catch (IllegalArgumentException e) { log.error(\"Invalid progress spec: {}\", e.getMessage()); }","preventionTips":["Centralize client-id parsing in one helper that filters blank entries","Give configuration an explicit non-empty default client list","Validate config-derived arrays before constructing specifications"],"tags":["java","mcp","configuration","empty-array"],"backgroundTag":"empty-required-field","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}