github/copilot-sdk · error · IllegalArgumentException
Parameter ' + param.name() + ' expected a numeric value for…
Error message
Parameter ' + param.name() + ' expected a numeric value for OptionalInt, got: + raw.getClass().getSimpleName()
What it means
ParamCoercion.coerce handles java.util.OptionalInt parameters by casting the raw argument to Number and calling intValue(). If the raw value is not a JSON number (e.g. a string "42", boolean, or object), the ClassCastException is rethrown as IllegalArgumentException explaining the parameter needed a numeric value for OptionalInt.
Solutions
- Ensure the tool's JSON Schema declares the parameter as {"type":"integer"} so models emit real numbers.
- Coerce obvious numeric strings yourself before dispatch: if raw is String, parse with Integer.parseInt and pass a Number.
- Change the parameter type to Optional<Integer> or String if mixed-type input is expected, and parse manually.
- Catch IllegalArgumentException from coerce and return a descriptive tool error so the model retries with a numeric value.
Example fix
// before
Object raw = args.get("count"); // "42" (string)
// coerce throws: expected a numeric value for OptionalInt
// after
if (raw instanceof String s) {
raw = Integer.valueOf(s.trim());
}
Object value = ParamCoercion.coerce(param, raw, mapper); Defensive patterns
Strategy: validation
Validate before calling
Object raw = args.get(param.name());
if (param.type() == OptionalInt.class && raw instanceof String s) {
raw = Integer.valueOf(s.trim()); // normalize stringified numbers
}
if (raw != null && !(raw instanceof Number)) {
throw new ToolInvocationException(param.name() + " must be an integer");
} Type guard
static boolean isNumericArg(Object raw) {
return raw instanceof Number ||
(raw instanceof String s && s.matches("-?\\d+"));
} Try / catch
try {
Object v = ParamCoercion.coerce(param, raw, mapper);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("expected a numeric value for OptionalInt")) {
return toolError(param.name() + " must be an integer, got: " + raw);
}
throw e;
} Prevention
- Declare integer parameters as {"type":"integer"} in the tool schema
- Normalize stringified numbers before coercion
- Prefer Optional<Integer> when clients may send loosely typed values
- Return descriptive tool errors so the model retries with correct types
When it happens
Trigger: A tool invocation supplies a non-numeric JSON value (string, boolean, array) for a parameter typed OptionalInt — commonly a stringified number like "42" produced by the model or by client-side serialization.
Common situations: Models quoting numeric arguments; config/CLI inputs passing strings; a schema declaring the parameter as string type while the Java signature uses OptionalInt; JavaScript clients sending numbers as strings.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Required parameter ' + param.name() + ' is missing from…
- Parameter ' + param.name() + ' expected a numeric value for…
- Parameter ' + param.name() + ' expected a numeric value for…
- Unknown AgentMode value: + value
- Unknown AskUserVariant value: + value
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/ac7e1dd0086ee943.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/rpc/ParamCoercion.java:82
if (raw == null) {
if (param.hasDefaultValue()) {
return coerceDefault(param, mapper);
} else if (!param.required()) {
return (T) emptyOptionalOrNull(param.type());
} else {
throw new IllegalArgumentException(
"Required parameter '" + param.name() + "' is missing from tool invocation");
}
}
Class<T> type = param.type();
// Handle Optional* types explicitly before delegating to ObjectMapper
if (type == java.util.OptionalInt.class) {
try {
return (T) java.util.OptionalInt.of(((Number) raw).intValue());
} catch (ClassCastException ex) {
throw new IllegalArgumentException("Parameter '" + param.name()
+ "' expected a numeric value for OptionalInt, got: " + raw.getClass().getSimpleName(), ex);
}
}
if (type == java.util.OptionalLong.class) {
try {
return (T) java.util.OptionalLong.of(((Number) raw).longValue());
} catch (ClassCastException ex) {
throw new IllegalArgumentException("Parameter '" + param.name()
+ "' expected a numeric value for OptionalLong, got: " + raw.getClass().getSimpleName(), ex);
}
}
if (type == java.util.OptionalDouble.class) {
try {
return (T) java.util.OptionalDouble.of(((Number) raw).doubleValue());
} catch (ClassCastException ex) {
throw new IllegalArgumentException("Parameter '" + param.name()
+ "' expected a numeric value for OptionalDouble, got: " + raw.getClass().getSimpleName(), ex);
}View on GitHub (pinned to cd8cf15dc3)