github/copilot-sdk · error · IllegalArgumentException

schema must be a valid JSON object string

Error message

schema must be a valid JSON object string (must start with '{' and end with '}')

What it means

When a Param is given a non-empty schema, the constructor validates that the schema string looks like a JSON object: trimmed text must start with '{' and end with '}'. Otherwise it throws this IllegalArgumentException. Only a lightweight shape check — it does not fully parse the JSON, so inner malformation is not caught here.

Solutions

  1. Wrap the schema in braces: "{\"type\": \"object\", ...}".
  2. Trim stray whitespace/quotes so the trimmed string begins with { and ends with }.
  3. Validate the schema with a JSON parser before constructing the Param to catch deeper errors early.

Example fix

// before
schema = "type: object, properties: { q: string }"
// after
schema = "{\"type\": \"object\", \"properties\": {\"q\": {\"type\": \"string\"}}}"
Defensive patterns

Strategy: validation

Validate before calling

static void assertSchemaObject(String schema) {
    if (schema != null && !schema.isEmpty()) {
        String t = schema.trim();
        if (!t.startsWith("{") || !t.endsWith("}"))
            throw new IllegalArgumentException("schema must be a JSON object string");
        new com.fasterxml.jackson.databind.ObjectMapper().readTree(t); // deeper check
    }
}

Type guard

static boolean isJsonObjectString(String s) {
    if (s == null) return false;
    String t = s.trim();
    return t.startsWith("{") && t.endsWith("}");
}

Try / catch

try {
    new Param(name, desc, type, null, false, schema);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("schema must be a valid JSON object")) {
        schema = "{" + schema.trim() + "}"; // repair missing braces, then retry
    } else throw e;
}

Prevention

When it happens

Trigger: new Param(..., schema="type: object") or schema="[]" or schema missing entirely from a JSON snippet (e.g. "type\": ...), i.e. any non-empty schema not delimited by braces.

Common situations: Passing a YAML or plain-text type description instead of a JSON schema, forgetting surrounding braces, wrapping the schema in quotes twice, or copying only the inner properties object without the outer {}.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/8cbaf8e6df6d927f. Report an issue: GitHub.

Appendix: source

Thrown at java/sdk/src/main/java/com/github/copilot/tool/Param.java:58

    private final String schema;

    private Param(Class<T> type, String name, String description, boolean required, String defaultValue,
            String schema) {
        this.type = Objects.requireNonNull(type, "type");
        this.name = requireNonBlank(name, "name");
        this.description = requireNonBlank(description, "description");
        this.defaultValue = defaultValue == null ? "" : defaultValue;
        this.schema = schema == null ? "" : schema;
        this.required = required;

        if (this.required && !this.defaultValue.isEmpty()) {
            throw new IllegalArgumentException("required=true cannot be combined with a non-empty defaultValue");
        }

        if (!this.schema.isEmpty()) {
            String trimmed = this.schema.trim();
            if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
                throw new IllegalArgumentException(
                        "schema must be a valid JSON object string (must start with '{' and end with '}')");
            }
            if (!this.defaultValue.isEmpty()) {
                throw new IllegalArgumentException(
                        "schema cannot be combined with defaultValue — express defaults inside the schema if needed");
            }
        }

        validateDefaultValue(type, this.defaultValue);
    }

    /**
     * Creates a required parameter with no default value.
     *
     * @param <T>
     *            the parameter type
     * @param type
     *            the Java class of the parameter

View on GitHub (pinned to cd8cf15dc3)