github/copilot-sdk · error · IllegalArgumentException

Invalid schema JSON for parameter ' + param.name() + ' in…

Error message

Invalid schema JSON for parameter ' + param.name() + ' in tool ' + toolName + ': + e.getMessage()

What it means

Thrown by ParamSchema.buildSchema when a Param supplies a custom schema() JSON string that cannot be parsed by Jackson (with FAIL_ON_TRAILING_TOKENS and USE_BIG_DECIMAL_FOR_FLOATS enabled). The message includes the parameter name, tool name, and Jackson's parse error, so invalid schema text is rejected at registration time instead of producing a broken tool schema.

Solutions

  1. Validate the schema string with a JSON linter/parse before registering the tool
  2. Ensure schema() is exactly one JSON object (no trailing tokens, no leading BOM)
  3. Generate the schema programmatically (forType / Map.of) instead of a hand-written string
  4. Read e.getMessage() in the error — it pinpoints the exact offset and reason Jackson failed

Example fix

// before
param.schema = "{\"type\":\"string\"} extra"; // trailing token
// after
param.schema = "{\"type\":\"string\"}";
Defensive patterns

Strategy: validation

Validate before calling

new ObjectMapper().readerFor(Map.class).readValue(param.schema()); // pre-validate custom schema JSON

Type guard

boolean isValidJsonObject(String s) { try { new ObjectMapper().readValue(s, Map.class); return true; } catch (Exception e) { return false; } }

Try / catch

try { schema = ParamSchema.buildSchema(toolName, params); } catch (IllegalArgumentException e) { /* e.getMessage() contains the Jackson parse error and offset */ throw new ToolRegistrationException(toolName, e); }

Prevention

When it happens

Trigger: Passing schema() text that is not a single valid JSON object — malformed JSON, trailing garbage after the object, multiple JSON values concatenated, or an array/scalar instead of an object map.

Common situations: Hand-authored JSON Schema snippets with missing braces or trailing commas; embedding JSON in properties files where escaping is lost; concatenating two schema strings.

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/4430c4d863a60368. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/rpc/ParamSchema.java:97

                throw new IllegalArgumentException(
                        "Duplicate parameter name '" + param.name() + "' in tool '" + toolName + "'");
            }
        }

        List<String> requiredNames = new ArrayList<>();
        Map<String, Object> properties = new LinkedHashMap<>();

        for (Param<?> param : params) {
            Map<String, Object> typeSchema;
            if (!param.schema().isEmpty()) {
                try {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> parsed = mapper.readerFor(Map.class)
                            .with(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
                            .with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS).readValue(param.schema());
                    typeSchema = parsed;
                } catch (Exception e) {
                    throw new IllegalArgumentException("Invalid schema JSON for parameter '" + param.name()
                            + "' in tool '" + toolName + "': " + e.getMessage(), e);
                }
            } else {
                typeSchema = forType(param.type());
            }
            Map<String, Object> enriched = new LinkedHashMap<>(typeSchema);
            enriched.put("description", param.description());
            if (param.hasDefaultValue()) {
                enriched.put("default", ParamCoercion.coerceDefault(param, mapper));
            }
            properties.put(param.name(), Collections.unmodifiableMap(enriched));
            if (param.required()) {
                requiredNames.add(param.name());
            }
        }

        return Map.of("type", "object", "properties", Collections.unmodifiableMap(properties), "required",
                Collections.unmodifiableList(requiredNames));

View on GitHub (pinned to cd8cf15dc3)