github/copilot-sdk · error · IllegalArgumentException
Unexpected end of JSON
Error message
Unexpected end of JSON
What it means
CopilotToolProcessor's peek() helper reads the current JSON character but throws IllegalArgumentException if the parser position is past the end of the input. This means the JSON ended abruptly while the parser still expected more content — the input is truncated or empty rather than merely malformed.
Solutions
- Ensure the full JSON string is received/parsed only after the stream completes.
- Check for empty or blank input and short-circuit before invoking the parser.
- Catch IllegalArgumentException and treat it as a truncation signal — request regeneration or completion of the tool call.
Example fix
// before parse(argsChunk); // partial stream // after if (argsChunk == null || argsChunk.isBlank()) throw new SkipException(); String complete = bufferUntilStreamClosed(argsChunk); parse(complete);
Defensive patterns
Strategy: validation
Validate before calling
if (args == null || args.isBlank()) {
throw new IllegalStateException("Tool arguments are empty — nothing to parse");
} Type guard
static boolean hasParseableInput(String s) { return s != null && !s.isBlank(); } Try / catch
try {
processor.parse(args);
} catch (IllegalArgumentException e) {
if ("Unexpected end of JSON".equals(e.getMessage())) {
return Result.incomplete(); // wait for stream completion / regenerate
}
throw e;
} Prevention
- Buffer streaming model output and parse only the complete string.
- Enforce a minimum non-blank length on tool-argument payloads.
- Set stream-size limits high enough that JSON is never cut mid-way.
When it happens
Trigger: Passing an empty string or a JSON prefix (e.g. {"name": "a", ) to the tool-argument parser; peek() is called when a token or structural character is required but input is exhausted.
Common situations: Streaming LLM responses parsed before completion, arguments truncated by a size limit, empty tool-call argument strings from the model.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
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/220b129d307ea599.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java:1170
}
private boolean isDigitOneToNine(char c) {
return c >= '1' && c <= '9';
}
private void skipWhitespace() {
while (pos < input.length() && isJsonWhitespace(input.charAt(pos))) {
pos++;
}
}
private boolean isJsonWhitespace(char c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
private char peek() {
if (pos >= input.length()) {
throw new IllegalArgumentException("Unexpected end of JSON");
}
return input.charAt(pos);
}
private void expect(char c) {
if (pos >= input.length() || input.charAt(pos) != c) {
throw new IllegalArgumentException("Expected '" + c + "' at position " + pos + " but got '"
+ (pos < input.length() ? input.charAt(pos) : "EOF") + "'");
}
pos++;
}
private boolean tryConsume(char c) {
if (pos < input.length() && input.charAt(pos) == c) {
pos++;
return true;
}
return false;View on GitHub (pinned to cd8cf15dc3)