github/copilot-sdk · error · IllegalArgumentException
Unterminated string escape at position
Error message
Unterminated string escape at position <pos>
What it means
parseEscape is invoked after a backslash inside a JSON string, but the input has already ended — the backslash was the last character, so no escape character follows. This means the string literal is truncated/malformed, and the converter reports the position of the dangling escape.
Solutions
- Escape backslashes properly: every literal \ in a JSON string must be written as \\ (so a path ending in \ becomes ...\\\\)
- Use a JSON serializer to build the string instead of manual escaping
- Check whether the payload was truncated in transit or by logging; re-fetch the complete payload
- Fix any hand-rolled escaping/unescape code that strips one backslash too many
Example fix
// before
String json = "{\"path\":\"C:\\tmp\\\"}"; // trailing single backslash
// after
String json = "{\"path\":\"C:\\tmp\\\"}".replace("\\\\\"", "\\\\\\\\\"");
// better: serialize with a JSON library
String json = new JSONObject().put("path", "C:\tmp\\").toString(); Defensive patterns
Strategy: validation
Validate before calling
if (json.trim().endsWith("\\")) {
throw new IllegalArgumentException("Payload ends with a dangling backslash; escaping is broken or input truncated");
} Try / catch
try {
String result = CopilotToolProcessor.jsonToMapOfSource(json);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Unterminated string escape")) {
// check payload completeness / re-encode backslashes
}
} Prevention
- Double every literal backslash in JSON strings (Windows paths: C:\\ -> C:\\\\)
- Use a JSON serializer for anything containing backslashes
- Verify payloads were not truncated by log buffers or network reads
- Never hand-strip escapes before parsing
When it happens
Trigger: A JSON string ending in a lone backslash, e.g. '{"path":"C:\\' where the final backslash is not itself escaped (should be '\\\\'), or a truncated string from a cut-off network payload.
Common situations: Windows file paths embedded in JSON where backslashes were not doubled; manually escaped strings where the trailing backslash was missed; truncated responses from a flaky stream or log capture.
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
- Invalid escape sequence \
- Incomplete Unicode escape at position <pos-2>
- Invalid Unicode escape at position <pos-1>
- Unexpected trailing content at position
- Unescaped control character at position <pos-1>
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/73f263b4df2b2a24.
Report an issue: GitHub.
Appendix: source
Thrown at java/sdk/src/main/java/com/github/copilot/tool/CopilotToolProcessor.java:1044
StringBuilder sb = new StringBuilder();
while (pos < input.length() && input.charAt(pos) != '"') {
char current = input.charAt(pos++);
if (current == '\\') {
sb.append(parseEscape());
} else {
if (current < 0x20) {
throw new IllegalArgumentException("Unescaped control character at position " + (pos - 1));
}
sb.append(current);
}
}
expect('"');
return sb.toString();
}
private char parseEscape() {
if (pos >= input.length()) {
throw new IllegalArgumentException("Unterminated string escape at position " + pos);
}
char escaped = input.charAt(pos++);
return switch (escaped) {
case '"', '\\', '/' -> escaped;
case 'b' -> '\b';
case 'f' -> '\f';
case 'n' -> '\n';
case 'r' -> '\r';
case 't' -> '\t';
case 'u' -> parseUnicodeEscape();
default -> throw new IllegalArgumentException(
"Invalid escape sequence \\" + escaped + " at position " + (pos - 2));
};
}
private char parseUnicodeEscape() {
if (pos + 4 > input.length()) {
throw new IllegalArgumentException("Incomplete Unicode escape at position " + (pos - 2));View on GitHub (pinned to cd8cf15dc3)