{"record":{"id":"92a820a2edd500d4","repo":"github/copilot-sdk","slug":"required-parameter-param-name-is-missing","errorCode":null,"errorMessage":"Required parameter ' + param.name() + ' is missing from tool invocation","messagePattern":"Required parameter ' \\+ param\\.name\\(\\) \\+ ' is missing from tool invocation","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"java/sdk/src/main/java/com/github/copilot/rpc/ParamCoercion.java","lineNumber":70,"sourceCode":"     * @param param\n     *            the parameter descriptor\n     * @param mapper\n     *            the configured {@link ObjectMapper} for complex type conversion\n     * @return the coerced argument value\n     * @throws IllegalArgumentException\n     *             if a required parameter is missing or coercion fails\n     */\n    @SuppressWarnings(\"unchecked\")\n    static <T> T coerce(Map<String, Object> args, Param<T> param, ObjectMapper mapper) {\n        Object raw = (args != null) ? args.get(param.name()) : null;\n\n        if (raw == null) {\n            if (param.hasDefaultValue()) {\n                return coerceDefault(param, mapper);\n            } else if (!param.required()) {\n                return (T) emptyOptionalOrNull(param.type());\n            } else {\n                throw new IllegalArgumentException(\n                        \"Required parameter '\" + param.name() + \"' is missing from tool invocation\");\n            }\n        }\n\n        Class<T> type = param.type();\n\n        // Handle Optional* types explicitly before delegating to ObjectMapper\n        if (type == java.util.OptionalInt.class) {\n            try {\n                return (T) java.util.OptionalInt.of(((Number) raw).intValue());\n            } catch (ClassCastException ex) {\n                throw new IllegalArgumentException(\"Parameter '\" + param.name()\n                        + \"' expected a numeric value for OptionalInt, got: \" + raw.getClass().getSimpleName(), ex);\n            }\n        }\n        if (type == java.util.OptionalLong.class) {\n            try {\n                return (T) java.util.OptionalLong.of(((Number) raw).longValue());","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/java/sdk/src/main/java/com/github/copilot/rpc/ParamCoercion.java#L52-L88","documentation":"ParamCoercion.coerce converts raw JSON tool-invocation arguments into typed Java parameters. When a raw argument is null and the ToolParameter is marked required (with no default value), the library throws IllegalArgumentException because a required tool parameter was not supplied by the model/caller.","triggerScenarios":"Invoking a registered tool where a @ToolParameter(required=true) argument is absent from the arguments object or explicitly null — e.g. the LLM omitted the key or sent {\"path\": null}.","commonSituations":"Models omitting optional-seeming arguments the developer marked required; schema/JSON-Schema not declaring the parameter required so the model drops it; callers invoking tools programmatically with incomplete maps; renaming a parameter so the old key no longer matches.","solutions":["Mark the parameter as required in the tool's JSON Schema (\"required\": [name]) so the model is forced to supply it.","Give the parameter a default value via hasDefaultValue/coerceDefault if it can reasonably be defaulted.","Validate the arguments map against the tool's parameter list before dispatching and return a clear tool error to the model.","If the parameter should be optional, change required=false or use Optional/nullable types so emptyOptionalOrNull is returned instead."],"exampleFix":"// before\n@ToolParameter(name = \"path\", required = true)\n// schema generated without required -> model omits it\n\n// after\n// ensure generated schema includes: {\"type\":\"object\",\"required\":[\"path\"],...}\n// and/or provide a default:\n@ToolParameter(name = \"path\", required = true, defaultValue = \".\")","handlingStrategy":"validation","validationCode":"for (ToolParameter p : tool.parameters()) {\n    if (p.required() && !p.hasDefaultValue() && !args.containsKey(p.name())) {\n        throw new ToolInvocationException(\"missing required argument: \" + p.name());\n    }\n}","typeGuard":"static boolean hasRequiredArgs(Map<String,Object> args, List<ToolParameter> params) {\n    return params.stream()\n        .filter(ToolParameter::required)\n        .filter(p -> !p.hasDefaultValue())\n        .allMatch(p -> args.get(p.name()) != null);\n}","tryCatchPattern":"try {\n    Object v = ParamCoercion.coerce(param, raw, mapper);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"is missing from tool invocation\")) {\n        return toolError(\"missing required parameter: \" + param.name());\n    }\n    throw e;\n}","preventionTips":["Emit \"required\": [param] in each tool's JSON Schema so models supply mandatory arguments","Validate the arguments map against the tool signature before dispatch","Provide defaultValue for parameters that can sensibly default","Return a structured tool error so the model can retry with the missing argument"],"tags":["java","rpc","tool-invocation","validation"],"backgroundTag":"missing-required-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"}