ComposioHQ/composio · error · JSONSchemaRefResolutionError

Cannot resolve $ref {pointer}

Error message

Cannot resolve $ref {pointer}

What it means

A JSON Schema $ref pointer (e.g. #/$defs/Foo) could not be resolved while dereference_json_schema was inlining the schema. The pointer's target is missing from the document or the JSON pointer is malformed. This fires in strict (default 'throw') mode; the SDK treats a dangling internal $ref as a bug in the schema producer.

Source

Thrown at python/composio/utils/json_schema.py:120

        elif isinstance(cursor, dict):
            if segment not in cursor:
                return _Resolution(ok=False, reason="missing-target", failed_at=segment)
            cursor = cursor[segment]
        else:
            return _Resolution(ok=False, reason="missing-target", failed_at=segment)
    return _Resolution(ok=True, value=cursor)


def _raise_resolution_error(pointer: str, resolution: _Resolution) -> t.NoReturn:
    if resolution.reason == "malformed-pointer":
        raise JSONSchemaRefResolutionError(
            f"Unsupported $ref pointer: {pointer}",
            meta={"ref": pointer},
        )
    meta: t.Dict[str, t.Any] = {"ref": pointer}
    if resolution.failed_at is not None:
        meta["failed_at"] = resolution.failed_at
    raise JSONSchemaRefResolutionError(
        f"Cannot resolve $ref {pointer}",
        meta=meta,
    )


def dereference_json_schema(
    schema: t.Any,
    *,
    on_unresolved: UnresolvedRefStrategy = "throw",
    on_replace: t.Optional[OnReplace] = None,
) -> t.Any:
    """Inline internal ``$ref`` pointers (``#/$defs/...`` and legacy
    ``#/definitions/...``), returning a new schema; the input is never mutated.

    External refs (``http://``, ``https://``, …) are left untouched (and logged
    once for audit, since a downstream resolver fetching them could enable SSRF
    or local-file disclosure). Cycles — both ``$ref`` cycles and JS-object-style
    identity cycles — are broken with a permissive ``{"type": "object",

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Fix the schema so every $ref target exists under $defs/definitions
  2. If the schema comes from an upstream service you cannot edit, call dereference_json_schema with on_unresolved='sentinel' to replace danglers with a permissive object
  3. Upgrade composio — newer versions default to sentinel degradation for API-sourced schemas
  4. If you own the schema, validate it with a JSON Schema library (jsonschema.check_schema) before passing it in

Example fix

# before
out = dereference_json_schema(schema)
# after
out = dereference_json_schema(schema, on_unresolved="sentinel")
Defensive patterns

Strategy: try-catch

Validate before calling

def has_dangling_refs(schema):
    import json
    defs = {**schema.get('$defs', {}), **schema.get('definitions', {})}
    text = json.dumps(schema)
    import re
    for m in re.finditer(r'"\$ref"\s*:\s*"#/\$defs/([^"]+)"', text):
        if m.group(1) not in defs:
            return True
    return False

Type guard

def is_resolvable_schema(s: dict) -> bool:
    defs = {**s.get('$defs', {}), **s.get('definitions', {})}
    def walk(n):
        if isinstance(n, dict):
            r = n.get('$ref')
            if isinstance(r, str) and r.startswith('#/$defs/'):
                if r.split('/')[-1] not in defs: return False
            return all(walk(v) for v in n.values())
        if isinstance(n, list):
            return all(walk(v) for v in n)
        return True
    return walk(s)

Try / catch

from composio.exceptions import JSONSchemaRefResolutionError
try:
    out = dereference_json_schema(schema, on_unresolved="sentinel")
except JSONSchemaRefResolutionError as e:
    logger.warning("unresolvable $ref: %s", e.meta)
    out = schema  # use raw schema with refs

Prevention

When it happens

Trigger: Calling dereference_json_schema (or APIs that use it: process_file_uploadable_schema, substitute_file_uploads, alias_tool_input_schema) on a schema whose $ref points at a #/$defs/... key that does not exist, or uses an escaped/malformed JSON pointer (~ escapes wrong, empty segments).

Common situations: The Composio API ships some output_parameters with a $ref into #/$defs but no $defs block (issue #3307); hand-written custom tool schemas with typos in $defs names; schemas truncated in transit.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/e77be0f3c9f44b6f. Report an issue: GitHub.