conductor-oss/conductor · error · UnsupportedSchemaException

{location}: uses unsupported JSON Schema keyword '{key}' at

Error message

{location}: uses unsupported JSON Schema keyword '{key}' at {path}. The PAC runtime validator implements a Draft-07 subset; keywords like $ref/allOf/oneOf/format would silently pass at runtime, producing permissive validation. Restrict the schema to: {SUPPORTED}.

What it means

SchemaSubsetValidator.validate throws UnsupportedSchemaException when a JSON Schema uses a keyword from the KNOWN_UNSUPPORTED set ($ref, $defs, definitions, allOf, anyOf, oneOf, not, if/then/else, dependencies, format, const, multipleOf, exclusiveMinimum/Maximum, uniqueItems, contains, patternProperties, contentEncoding/MediaType/Schema, readOnly/writeOnly, etc.). The runtime inline validator (JavaScriptBuilder.schemaValidatorScript) implements only a Draft-07 subset; using an unsupported keyword would silently pass at runtime, so this check turns that into a loud compile-time rejection.

Source

Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/util/SchemaSubsetValidator.java:131

     * Walk {@code schema} recursively, throwing {@link UnsupportedSchemaException} on the first
     * unsupported keyword encountered. {@code location} is a human-readable label (e.g. "tool
     * 'write_file' inputSchema") prepended to the error message.
     *
     * <p>A {@code null} or non-Map schema is treated as "no schema" — the runtime path handles it
     * the same way (early-return without validation), so accept it here too.
     */
    public static void validate(Map<String, Object> schema, String location) {
        if (schema == null || schema.isEmpty()) return;
        validateInternal(schema, location, "");
    }

    @SuppressWarnings("unchecked")
    private static void validateInternal(Map<String, Object> schema, String location, String path) {
        for (Map.Entry<String, Object> e : schema.entrySet()) {
            String key = e.getKey();
            if (SUPPORTED.contains(key)) continue;
            if (KNOWN_UNSUPPORTED.contains(key)) {
                throw new UnsupportedSchemaException(
                        location
                                + ": uses unsupported JSON Schema keyword '"
                                + key
                                + "' at "
                                + (path.isEmpty() ? "<root>" : path)
                                + ". The PAC runtime validator implements a Draft-07 subset; "
                                + "keywords like $ref/allOf/oneOf/format would silently pass at "
                                + "runtime, producing permissive validation. Restrict the schema "
                                + "to: "
                                + String.join(", ", SUPPORTED)
                                + ".");
            }
            // Unknown keyword — not in either set. Could be a typo, could be
            // a custom extension. Either way, ambiguous behaviour at runtime:
            // reject loudly.
            throw new UnsupportedSchemaException(
                    location
                            + ": unknown JSON Schema keyword '"

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Inline any `$ref`/`$defs` so the schema is fully flattened into `properties`.
  2. Replace `allOf`/`oneOf`/`anyOf` with explicit per-field constraints the subset validator supports (type, enum, pattern, min/maxLength, min/maximum, min/maxItems, required).
  3. Remove `format` — validate email/uri/etc. in your handler instead of via JSON Schema.
  4. Drop `const`/`default`/`examples`-adjacent unsupported keywords and enforce in code.

Example fix

// before
{
  "allOf": [
    {"type": "object"},
    {"required": ["email"]}
  ],
  "properties": {"email": {"type": "string", "format": "email"}}
}
// after (subset only)
{
  "type": "object",
  "required": ["email"],
  "properties": {"email": {"type": "string", "pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"}}
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate a schema against the runtime subset *before* publishing the tool.
try {
    SchemaSubsetValidator.validate(schemaMap, "tool/my-tool");
} catch (UnsupportedSchemaException e) {
    throw new IllegalArgumentException(e.getMessage(), e);
}

Type guard

boolean isSubsetSchema(Map<String,Object> schema) {
    try { SchemaSubsetValidator.validate(schema, "precheck"); return true; }
    catch (UnsupportedSchemaException e) { return false; }
}

Try / catch

try {
    SchemaSubsetValidator.validate(schema, location);
} catch (UnsupportedSchemaException e) {
    return badRequest(e.getMessage()); // tell the author which keyword at which path
}

Prevention

When it happens

Trigger: Authoring a tool's input schema with `$ref`/`$defs` for reuse, `allOf`/`oneOf` for composition, or `format: email` for hinting. validate() recurses, hits the keyword in KNOWN_UNSUPPORTED, and throws with the location and JSON path.

Common situations: Author copy-pasted a full Draft-07 schema from another tool; used `$ref` to keep schemas DRY; added `format` expecting email/uri validation at runtime.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/2e52e3b2f6bf9778. Report an issue: GitHub.