PrefectHQ/fastmcp · error · ValueError

External or non-local reference not supported: {ref_str}

Error message

External or non-local reference not supported: {ref_str}

What it means

OpenAPIParser._resolve_ref only supports local JSON-pointer references that begin with '#/' (references within the same document). When a component's $ref points anywhere else — another file, an absolute URL, or any non-local string — the parser raises ValueError because cross-document resolution is not implemented. If the ref is not a string at all, the item is returned unresolved instead (no error).

Source

Thrown at fastmcp_slim/fastmcp/utilities/openapi/parser.py:169

            "query": "query",
            "header": "header",
            "cookie": "cookie",
        }
        if location := locations.get(param_in):
            return location
        logger.warning(f"Unknown parameter location: {param_in}, defaulting to 'query'")
        return "query"

    def _resolve_ref(self, item: Any) -> Any:
        """Resolves a reference to its target definition."""
        if isinstance(item, self.reference_cls):
            ref_str = item.ref
            # Ensure ref_str is a string before calling startswith()
            if not isinstance(ref_str, str):
                return item
            try:
                if not ref_str.startswith("#/"):
                    raise ValueError(
                        f"External or non-local reference not supported: {ref_str}"
                    )

                parts = ref_str.strip("#/").split("/")
                target = self.openapi

                for part in parts:
                    if part.isdigit() and isinstance(target, list):
                        target = target[int(part)]
                    elif isinstance(target, BaseModel):
                        # Check class fields first, then model_extra
                        if part in target.__class__.model_fields:
                            target = getattr(target, part, None)
                        elif target.model_extra and part in target.model_extra:
                            target = target.model_extra[part]
                        else:
                            # Special handling for components
                            if part == "components" and hasattr(target, "components"):

View on GitHub (pinned to 1f02114297)

Solutions

  1. Bundle the spec into a single self-contained document before parsing: `npx @redocly/cli bundle spec.yaml -o bundled.json` (or swagger-cli bundle) so all refs become '#/components/...'.
  2. Inline external schemas manually: copy the referenced definitions into components/schemas and rewrite the $ref to '#/components/schemas/Name'.
  3. If the external ref points to a stable URL, pre-fetch it and merge it into your spec at load time with a small script before calling the parser.
  4. Regenerate the spec from the source (e.g. FastAPI) ensuring a single-file export without external refs.

Example fix

// before (in spec.yaml)
$ref: 'https://api.example.com/schemas/User.json'

// after
$ref: '#/components/schemas/User'  # definition inlined into the same document
Defensive patterns

Strategy: validation

Validate before calling

import json

def find_external_refs(node, out=None):
    out = [] if out is None else out
    if isinstance(node, dict):
        ref = node.get("$ref")
        if isinstance(ref, str) and not ref.startswith("#/"):
            out.append(ref)
        for v in node.values():
            find_external_refs(v, out)
    elif isinstance(node, list):
        for v in node:
            find_external_refs(v, out)
    return out

external = find_external_refs(spec)
assert not external, f"Bundle first, external refs: {external}"

Type guard

def is_local_ref(ref: object) -> bool:
    return isinstance(ref, str) and ref.startswith("#/")

Try / catch

try:
    routes = parse_openapi_to_http_routes(spec)
except ValueError as e:
    if "External or non-local reference" in str(e):
        raise SystemExit("Bundle the spec (e.g. redocly bundle) and retry") from e
    raise

Prevention

When it happens

Trigger: Parsing a spec whose schemas/parameters/responses contain $ref values like 'https://api.example.com/schemas/User.json', 'common.yaml#/components/schemas/X', or './types.json#/Foo' while running _extract_schema_as_dict, _extract_parameters, _extract_request_body, _extract_responses, or parse.

Common situations: Specs split across multiple files by a bundler that wasn't run (source refs left in); vendor-published specs referencing external schema URLs; manually edited specs with copied external refs; codegen output using relative file refs.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/7981aa99b1c97c33. Report an issue: GitHub.