github/copilot-sdk · error · IllegalArgumentException
Number cannot be represented at position
Error message
Number cannot be represented at position <start>: <number>
What it means
After lexing a numeric token, CopilotToolProcessor validates it by constructing a java.math.BigDecimal from the substring. If BigDecimal rejects the text (NumberFormatException), the parser rethrows IllegalArgumentException stating the number cannot be represented, wrapping the original exception. This guards against malformed numeric literals that slip past the grammar check.
Solutions
- Inspect the quoted number in the message and correct it to a valid JSON numeric literal.
- Re-send the tool call with the argument value regenerated as a plain decimal/exponent form.
- Catch IllegalArgumentException (getCause() is NumberFormatException) and fall back to a JSON repair pass before parsing.
Example fix
// before
{"amount": 1_000}
// after
{"amount": 1000} Defensive patterns
Strategy: try-catch
Validate before calling
static boolean isRepresentableNumber(String token) {
try { new java.math.BigDecimal(token); return true; }
catch (NumberFormatException e) { return false; }
} Type guard
static java.math.BigDecimal asBigDecimalOrNull(String token) {
try { return new java.math.BigDecimal(token.trim()); }
catch (NumberFormatException e) { return null; }
} Try / catch
try {
processor.parse(args);
} catch (IllegalArgumentException e) {
if (e.getCause() instanceof NumberFormatException nfe) {
// retry after sanitizing the numeric token named in e.getMessage()
}
throw e;
} Prevention
- Sanitize numeric tokens (strip underscores, spaces, locale group separators) before sending tool calls.
- Prefer emitting numbers through a serializer rather than templates.
- Check for truncation when arguments arrive over a stream.
When it happens
Trigger: A number token that passed the start-character check but is not BigDecimal-parseable, e.g. an empty token, or a value with characters the grammar accidentally allowed. Wraps NumberFormatException from new java.math.BigDecimal(number) at position start.
Common situations: Exotic or malformed numeric literals in LLM tool-call output, truncated JSON from a stream cut mid-number, locale-independent formatting issues in generated arguments.
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
- Expected digit in number
- Unexpected trailing content at position
- Unescaped control character at position <pos-1>
- Unterminated string escape at position
- Invalid escape sequence \
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/46cfc192f7331de9.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java:1132
}
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) {
throw new IllegalArgumentException("Number cannot be represented at position " + start + ": " + number,
e);
}
return "new java.math.BigDecimal(\"" + number + "\")";
}
private void requireDigit(String part) {
if (pos >= input.length() || !isAsciiDigit(input.charAt(pos))) {
throw new IllegalArgumentException("Expected digit in number " + part + " at position " + pos);
}
}
private void consumeDigits() {
while (pos < input.length() && isAsciiDigit(input.charAt(pos))) {
pos++;
}
}
private boolean isAsciiDigit(char c) {View on GitHub (pinned to cd8cf15dc3)