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

  1. Ensure the tool's JSON Schema declares the parameter as {"type":"integer"} so models emit real numbers.
  2. Coerce obvious numeric strings yourself before dispatch: if raw is String, parse with Integer.parseInt and pass a Number.
  3. Change the parameter type to Optional<Integer> or String if mixed-type input is expected, and parse manually.
  4. 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

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


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)