zed-industries/zed · error · anyhow::Error

Unsupported $ref format (only `#/$defs/<name>` and `#/defini

Error message

Unsupported $ref format (only `#/$defs/<name>` and `#/definitions/<name>` are supported): {ref_str}

What it means

parse_ref only accepts same-document references of the exact forms #/$defs/<name> and #/definitions/<name>. Any other $ref string — external file refs like 'shared.json#/Foo', JSON-Schema-2020 anchors like '#Foo', or OpenAPI-style '#/components/schemas/Foo' — is rejected before lookup because the inliner has no way to fetch or resolve it.

Source

Thrown at crates/language_model_core/src/tool_schema.rs:198

                resolve_refs_recursive(item, defs, legacy_defs, visiting)?;
            }
        }
        _ => {}
    }
    Ok(())
}

/// Parses a same-document `$ref` like `#/$defs/Foo` or `#/definitions/Foo`.
/// Returns `(defs_key, name)` where `defs_key` is the top-level key the
/// definition was looked up under, and `name` is the definition name.
fn parse_ref(ref_str: &str) -> Result<(&'static str, &str)> {
    if let Some(name) = ref_str.strip_prefix("#/$defs/") {
        return Ok(("$defs", name));
    }
    if let Some(name) = ref_str.strip_prefix("#/definitions/") {
        return Ok(("definitions", name));
    }
    anyhow::bail!(
        "Unsupported $ref format (only `#/$defs/<name>` and `#/definitions/<name>` are supported): {ref_str}"
    );
}

fn adapt_to_json_schema_subset(json: &mut Value) -> Result<()> {
    if let Value::Object(obj) = json {
        const UNSUPPORTED_KEYS: [&str; 4] = ["if", "then", "else", "$ref"];

        for key in UNSUPPORTED_KEYS {
            anyhow::ensure!(
                !obj.contains_key(key),
                "Schema cannot be made compatible because it contains \"{key}\""
            );
        }

        const KEYS_TO_REMOVE: [(&str, fn(&Value) -> bool); 6] = [
            ("format", |value| value.is_string()),
            ("additionalProperties", |_| true),

View on GitHub (pinned to f4178619ac)

Solutions

  1. Pre-process the schema with an external dereferencer (jsonschema-ref-parser, datamodel-code-generator) that produces one self-contained document
  2. Rewrite OpenAPI '#/components/schemas/X' refs to '#/$defs/X' and hoist the components into $defs
  3. Replace anchor-based refs with named $defs entries
  4. After bundling, re-check for stray $ref keys — adapt_to_json_schema_subset also hard-rejects any remaining $ref

Example fix

// before
{ "$ref": "shared.json#/Point" }

// after
{
  "$defs": { "Point": { "type": "object", "properties": { "x": { "type": "number" }, "y": { "type": "number" } } } },
  "$ref": "#/$defs/Point"
}
Defensive patterns

Strategy: validation

Validate before calling

fn only_supported_refs(schema: &serde_json::Value) -> bool {
    let ok = true;
    walk_refs(schema, &|r| r.starts_with("#/$defs/") || r.starts_with("#/definitions/"));
    ok
}
// bundle first if this returns false:
//   $RefParser.dereference(schema)  (JS)  |  jsonschema_ref_resolver (Rust)

Type guard

fn is_same_document_ref(ref_str: &str) -> bool {
    ref_str.starts_with("#/$defs/") || ref_str.starts_with("#/definitions/")
}

Prevention

When it happens

Trigger: Passing a tool input_schema authored against JSON Schema 2019+ (anchor-based refs), a multi-file schema split across $id documents, or a schema converted from OpenAPI without rewriting component paths.

Common situations: Copy-pasting schemas from OpenAPI specs; using $defs with nested paths like '#/$defs/Foo/properties/bar' (also unsupported since only the top-level name is parsed); monorepos sharing schema files by relative path.

Related errors


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/6f7a5176b4489b3f. Report an issue: GitHub.