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

$ref target not found in {defs_key}: {ref_str}

Error message

$ref target not found in {defs_key}: {ref_str}

What it means

When adapting a tool's JSON Schema for an LLM, tool_schema inlines every $ref by looking the name up under the top-level $defs (or legacy definitions) map. If the referenced definition is absent from whichever key the ref parsed into, resolution fails with this error naming the defs key and the full ref string.

Source

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

) -> Result<()> {
    match value {
        Value::Object(obj) => {
            if let Some(ref_str) = obj.get("$ref").and_then(|v| v.as_str()) {
                // Guard against cycles (A -> B -> A, or self-referential
                // schemas like a Tree node whose children are Trees)
                if visiting.iter().any(|v| v == ref_str) {
                    *obj = Map::new();
                    return Ok(());
                }

                let (defs_key, name) = parse_ref(ref_str)?;
                let defs_for_key = match defs_key {
                    "$defs" => defs,
                    "definitions" => legacy_defs,
                    _ => None,
                };
                let Some(def) = defs_for_key.and_then(|defs| defs.get(name)) else {
                    anyhow::bail!("$ref target not found in {defs_key}: {ref_str}");
                };

                let ref_owned = ref_str.to_string();

                // Inline the referenced definition into the current object.
                let mut resolved = def.clone();
                if let Value::Object(resolved_obj) = &mut resolved {
                    for (key, val) in obj.iter() {
                        if key != "$ref" {
                            resolved_obj.insert(key.clone(), val.clone());
                        }
                    }
                }
                *value = resolved;

                visiting.push(ref_owned);
                let result = resolve_refs_recursive(value, defs, legacy_defs, visiting);
                visiting.pop();

View on GitHub (pinned to f4178619ac)

Solutions

  1. Add the missing definition to the same top-level key the ref points at ($defs vs definitions must match exactly)
  2. Run the schema through a $ref bundler (e.g. jsonschema-ref-parser dereference) before passing it to the tool
  3. Inline the referenced object directly and drop the $ref
  4. Validate that the ref name has no typo/case mismatch with the def key

Example fix

// before
{
  "type": "object",
  "properties": { "cfg": { "$ref": "#/$defs/Config" } }
}

// after
{
  "type": "object",
  "properties": { "cfg": { "$ref": "#/$defs/Config" } },
  "$defs": {
    "Config": { "type": "object", "properties": { "name": { "type": "string" } } }
  }
}
Defensive patterns

Strategy: validation

Validate before calling

fn refs_resolvable(schema: &serde_json::Value) -> bool {
    let Some(map) = schema.as_object() else { return true };
    let defs = map.get("$defs").or_else(|| map.get("definitions"));
    walk_refs(schema, &|r| {
        let Ok((key, name)) = parse_ref(r) else { return false };
        defs.map(|d| d.get(name).is_some() && ((key == "$defs") == map.contains_key("$defs"))).unwrap_or(false)
    })
}

Type guard

fn is_inlineable_ref(ref_str: &str, defs: Option<&serde_json::Map<String, serde_json::Value>>) -> bool {
    matches!(parse_ref(ref_str), Ok((_, name))) && defs.is_some_and(|d| d.get(name).is_some())
}

Prevention

When it happens

Trigger: A tool input_schema containing "{$ref": "#/$defs/Foo"} where Foo is missing, defined under definitions while the ref points at $defs (or vice versa), or where a build step stripped the $defs block from the schema before it reached the LLM request.

Common situations: Hand-written tool schemas referencing shared types that were never inlined; serde(schemars)-generated schemas where the definition map was flattened/renamed; editing a tool schema and forgetting to move its defs along.

Related errors


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