github/copilot-sdk · error · IllegalArgumentException
Unexpected trailing content at position
Error message
Unexpected trailing content at position <pos>: '<rest>'
What it means
jsonToMapOfSource parses a JSON object string with a hand-rolled JsonToSourceConverter and, after successfully parsing the top-level object, checks that the entire input has been consumed. Any non-whitespace characters left after the closing brace of the root object cause this IllegalArgumentException. The library is strict: the input must be exactly one JSON value with no extra text.
Solutions
- Ensure the input is exactly one JSON object with nothing after the closing brace
- If handling multiple JSON documents, split them (one per line for NDJSON) and call jsonToMapOfSource on each individually
- Trim obvious junk, but note the parser already skips surrounding whitespace — look for real characters after the object
- Validate the string with a standard JSON parser first to confirm it is a single well-formed value
Example fix
// before
String json = line1 + line2; // two concatenated JSON objects
String result = CopilotToolProcessor.jsonToMapOfSource(json);
// after
for (String part : line.split("\n")) {
if (!part.isBlank()) {
String result = CopilotToolProcessor.jsonToMapOfSource(part);
}
} Defensive patterns
Strategy: validation
Validate before calling
String trimmed = json.trim();
if (!trimmed.startsWith("{")) throw new IllegalArgumentException("Input must be a single JSON object");
// optionally: new JSONObject(trimmed) to validate it is one complete value Type guard
static boolean isSingleJsonObject(String s) {
String t = s == null ? "" : s.trim();
return t.startsWith("{") && t.endsWith("}");
} Try / catch
try {
String result = CopilotToolProcessor.jsonToMapOfSource(json);
} catch (IllegalArgumentException e) {
// log position + rest of input to identify trailing junk
} Prevention
- Never concatenate JSON documents; parse them one at a time (NDJSON: split on newlines)
- Feed the parser exactly one JSON value; strip logging wrappers before parsing
- Round-trip validate with a standard JSON parser before calling custom parsers
- Sanitize copied/pasted tool output for prefixes/suffixes
When it happens
Trigger: Passing a string to jsonToMapOfSource that contains a valid JSON object followed by extra content, e.g. two concatenated JSON objects '{"a":1}{"b":2}', trailing text like '{"a":1} extra', or a JSON array when an object is expected (leading content the object parser rejects differently).
Common situations: Pasting/concatenating multiple JSON documents (NDJSON fed in whole); appending a semicolon or newline-plus-comment from copied code; logging frameworks wrapping the payload; tool output that includes a prefix/suffix around the JSON.
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
- Unescaped control character at position <pos-1>
- Unterminated string escape at position
- Invalid escape sequence \
- Incomplete Unicode escape at position <pos-2>
- Invalid Unicode escape at position <pos-1>
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/93a91af738fb07ec.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java:946
}
}
return sb.toString();
}
// ------------------------------------------------------------------
// JSON-to-Java source code conversion
// ------------------------------------------------------------------
/**
* Converts a JSON object string to a Java source expression. Supports nested
* objects, arrays, strings, numbers, booleans, and null.
*/
static String jsonToMapOfSource(String json) {
JsonToSourceConverter converter = new JsonToSourceConverter(json);
String result = converter.parseObject();
converter.skipWhitespace();
if (converter.pos < json.length()) {
throw new IllegalArgumentException("Unexpected trailing content at position " + converter.pos + ": '"
+ json.substring(converter.pos) + "'");
}
return result;
}
/**
* Minimal recursive-descent JSON parser that produces helper calls and literal
* Java source expressions from a JSON string. Only used at compile time by the
* annotation processor.
*/
private static final class JsonToSourceConverter {
private final String input;
private int pos;
JsonToSourceConverter(String input) {
this.input = input;
this.pos = 0;View on GitHub (pinned to cd8cf15dc3)