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

  1. Ensure the input is exactly one JSON object with nothing after the closing brace
  2. If handling multiple JSON documents, split them (one per line for NDJSON) and call jsonToMapOfSource on each individually
  3. Trim obvious junk, but note the parser already skips surrounding whitespace — look for real characters after the object
  4. 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

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


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)