{"record":{"id":"a0fa59e201b5914c","repo":"github/copilot-sdk","slug":"a-param-descriptor-is-null-for-tool-toolname","errorCode":null,"errorMessage":"A Param descriptor is null for tool ' + toolName + '","messagePattern":"A Param descriptor is null for tool ' \\+ toolName \\+ '","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"java/sdk/src/main/java/com/github/copilot/rpc/ParamSchema.java","lineNumber":76,"sourceCode":"     *            the configured {@link ObjectMapper} used to coerce default values\n     *            into their typed form for the schema\n     * @param params\n     *            zero or more parameter descriptors\n     * @return a JSON Schema object map with {@code type=object},\n     *         {@code properties}, and {@code required} keys\n     * @throws IllegalArgumentException\n     *             if a null param or duplicate parameter names are found\n     */\n    static Map<String, Object> buildSchema(String toolName, ObjectMapper mapper, Param<?>... params) {\n        if (params == null || params.length == 0) {\n            return Map.of(\"type\", \"object\", \"properties\", Map.of(), \"required\", List.of());\n        }\n\n        // Validate: no null params, no duplicate names\n        Set<String> seen = new HashSet<>();\n        for (Param<?> param : params) {\n            if (param == null) {\n                throw new IllegalArgumentException(\"A Param descriptor is null for tool '\" + toolName + \"'\");\n            }\n            if (!seen.add(param.name())) {\n                throw new IllegalArgumentException(\n                        \"Duplicate parameter name '\" + param.name() + \"' in tool '\" + toolName + \"'\");\n            }\n        }\n\n        List<String> requiredNames = new ArrayList<>();\n        Map<String, Object> properties = new LinkedHashMap<>();\n\n        for (Param<?> param : params) {\n            Map<String, Object> typeSchema;\n            if (!param.schema().isEmpty()) {\n                try {\n                    @SuppressWarnings(\"unchecked\")\n                    Map<String, Object> parsed = mapper.readerFor(Map.class)\n                            .with(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)\n                            .with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS).readValue(param.schema());","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/java/sdk/src/main/java/com/github/copilot/rpc/ParamSchema.java#L58-L94","documentation":"Thrown by ParamSchema.buildSchema during validation when the params array passed for a tool contains a null element. buildSchema validates that every Param descriptor is non-null and that names are unique before generating the JSON schema. It is a fail-fast guard against tool registration bugs.","triggerScenarios":"Calling buildSchema(toolName, params) with a list containing null — e.g. a Param created conditionally and left null, or arrays assembled with placeholder nulls.","commonSituations":"Programmatic tool registration where a factory method returns null; refactors that remove a Param but leave a null slot; reflection/annotation-processor gaps producing null descriptors.","solutions":["Remove null entries before calling buildSchema: params.removeIf(Objects::isNull)","Fix the factory/builder that produced a null Param instead of a descriptor","Assert all params non-null in tool registration tests","Catch IllegalArgumentException and log toolName to identify the misregistered tool"],"exampleFix":"// before\nList<Param<?>> params = Arrays.asList(p1, null, p3);\nParamSchema.buildSchema(\"myTool\", params); // throws\n// after\nList<Param<?>> params = Arrays.asList(p1, p3);\nParamSchema.buildSchema(\"myTool\", params);","handlingStrategy":"validation","validationCode":"if (params.stream().anyMatch(Objects::isNull)) { throw new IllegalStateException(\"null Param in \" + toolName); }","typeGuard":"boolean hasNoNullParams(List<Param<?>> ps) { return ps != null && ps.stream().noneMatch(Objects::isNull); }","tryCatchPattern":"try { schema = ParamSchema.buildSchema(toolName, params); } catch (IllegalArgumentException e) { throw new ToolRegistrationException(toolName, e); }","preventionTips":["Filter nulls with params.removeIf(Objects::isNull) before registration","Make Param factories non-null-returning (throw internally instead)","Add registration unit tests for every tool"],"tags":["java","rpc","null-argument","tool-schema"],"backgroundTag":"null-argument","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}