github/copilot-sdk · error · IllegalArgumentException

Expected number at position

Error message

Expected number at position <pos>

What it means

The CopilotToolProcessor contains a hand-rolled JSON tokenizer that converts tool-call argument JSON into Java expressions. While parsing a number literal, the first character must be '0' or a digit 1-9; anything else (e.g. a letter, '+', or a bare '-') makes the parser throw this IllegalArgumentException, since JSON grammar disallows numbers beginning any other way.

Solutions

  1. Fix the JSON so the number starts with a digit 0-9 (use 0.5, not +0.5 or .5).
  2. Validate/repair the argument JSON with a standard parser (e.g. Jackson) before invoking the tool.
  3. Catch IllegalArgumentException around tool invocation and surface the position info (pos) to locate the malformed token.

Example fix

// before
{"retries": +3}
// after
{"retries": 3}
Defensive patterns

Strategy: validation

Validate before calling

static boolean validNumberStart(String json) {
    java.util.regex.Matcher m = java.util.regex.Pattern.compile("\\"-?\\d+(\\.\\d+)?([eE][+-]?\\d+)?\\"").matcher(json);
    return m.find();
}
// or simply: try { new com.fasterxml.jackson.databind.ObjectMapper().readTree(args); } catch (Exception e) { /* reject */ }

Type guard

static boolean isJsonNumberStart(String s, int i) {
    if (i < 0 || i >= s.length()) return false;
    char c = s.charAt(i);
    return c == '-' || (c >= '0' && c <= '9');
}

Try / catch

try {
    processor.parse(args);
} catch (IllegalArgumentException e) {
    log.warn("Malformed tool args: {}", e.getMessage());
    return Result.rejected(args, e.getMessage());
}

Prevention

When it happens

Trigger: Passing tool-call arguments where a numeric field starts with an invalid character: e.g. {"count": +1}, {"count": -} (lone minus reaching the number rule via a path that requires this branch), or a non-numeric token like {"count": abc} where a number was expected.

Common situations: LLM-generated tool arguments containing malformed JSON numbers, hand-written tool invocations with leading '+' or stray characters, template substitution leaving placeholder text where a number should be.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/61dfcb4c2ad238ea. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java:1113

                return "(Object) null";
            }
            throw new IllegalArgumentException("Expected null at position " + pos);
        }

        private String parseNumber() {
            int start = pos;
            if (pos < input.length() && input.charAt(pos) == '-') {
                pos++;
            }
            if (pos >= input.length()) {
                throw new IllegalArgumentException("Expected number at position " + start);
            }
            if (input.charAt(pos) == '0') {
                pos++;
            } else if (isDigitOneToNine(input.charAt(pos))) {
                consumeDigits();
            } else {
                throw new IllegalArgumentException("Expected number at position " + pos);
            }
            if (pos < input.length() && input.charAt(pos) == '.') {
                pos++;
                requireDigit("fraction");
                consumeDigits();
            }
            if (pos < input.length() && (input.charAt(pos) == 'e' || input.charAt(pos) == 'E')) {
                pos++;
                if (pos < input.length() && (input.charAt(pos) == '+' || input.charAt(pos) == '-')) {
                    pos++;
                }
                requireDigit("exponent");
                consumeDigits();
            }
            String number = input.substring(start, pos);
            try {
                new java.math.BigDecimal(number);
            } catch (NumberFormatException e) {

View on GitHub (pinned to cd8cf15dc3)