Skip to content

"Circular reference detected" when requesting the swagger.json file #2095

@RobertBlackhart

Description

@RobertBlackhart

After upgrading to connexion 3.3.0, we found that we would get a "Circular reference detected" error when trying to perform a GET request on the swagger.json file/endpoint. Here's a minimal example of the kind of spec that leads to this issue:

import copy
from importlib import metadata

from connexion import AsyncApp
from connexion.spec import Specification

spec = {
  "basePath": "/api/v1",
  "swagger": "2.0",
  "info": {
    "version": "1.0.0",
    "title": "Simple API"
  },
  "definitions": {
    "widget": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "owners": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/owner"
          }
        }
      }
    },
    "owner": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "widgets": {
          "type": "array",
          "items": {
            "$ref": "#/definitions/widget"
          }
        }
      }
    }
  },
  "paths": {
    "/owners": {
      "get": {
        "description": "Return a list of all registered owners",
        "operationId": "simple_app.get_owners",
        "responses": {
          "200": {
            "description": "A list of owners",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/owner"
              }
            }
          }
        }
      }
    }
  }
}

def get_owners():
    return [{"name": "Alice"}, {"name": "Bob"}]

print(f"Connexion version: {metadata.version('connexion')}")
if int(metadata.version("connexion").split(".")[1]) < 3:
    print("Applying monkeypatch to work around https://github.com/spec-first/connexion/issues/2028")
    Specification.clone = lambda self: type(self)(copy.deepcopy(self._raw_spec))

app = AsyncApp(__name__)
app.add_api(spec, validate_responses=False)

Running this application under connexion 3.2.0 (with the included monkeypatch for issue #2028) results in a success. Here's the application logs:

$ python -m uvicorn simple_app:app
Connexion version: 3.2.0
Applying monkeypatch to work around https://github.com/spec-first/connexion/issues/2028
INFO:     Started server process [3598565]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:58120 - "GET /api/v1/owners HTTP/1.1" 200 OK
INFO:     127.0.0.1:50990 - "GET /api/v1/swagger.json HTTP/1.1" 200 OK

And here's the HTTP request/response logs:

$ curl -i http://localhost:8000/api/v1/owners
HTTP/1.1 200 OK
date: Mon, 27 Oct 2025 12:39:30 GMT
server: uvicorn
content-type: application/json
content-length: 37

[{"name": "Alice"}, {"name": "Bob"}]
$ curl -i http://localhost:8000/api/v1/swagger.json
HTTP/1.1 200 OK
date: Mon, 27 Oct 2025 12:39:31 GMT
server: uvicorn
content-length: 715
content-type: application/json

{"basePath": "/api/v1", "swagger": "2.0", "info": {"version": "1.0.0", "title": "Simple API"}, "definitions": {"widget": {"type": "object", "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "owners": {"type": "array", "items": {"$ref": "#/definitions/owner"}}}}, "owner": {"type": "object", "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "widgets": {"type": "array", "items": {"$ref": "#/definitions/widget"}}}}}, "paths": {"/owners": {"get": {"description": "Return a list of all registered owners", "operationId": "simple_app.get_owners", "responses": {"200": {"description": "A list of owners", "schema": {"type": "array", "items": {"$ref": "#/definitions/owner"}}}}}}}}
$

But upgrading to connexion 3.3.0 runs into this error for the same GET on /swagger.json:

$ python -m uvicorn simple_app:app
Connexion version: 3.3.0
INFO:     Started server process [3598595]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
ValueError('Circular reference detected')
Traceback (most recent call last):
  File "~/pyenv/lib/python3.12/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
    await app(scope, receive, sender)
  File "~/pyenv/lib/python3.12/site-packages/starlette/routing.py", line 75, in app
    response = await f(request)
               ^^^^^^^^^^^^^^^^
  File "~/pyenv/lib/python3.12/site-packages/connexion/middleware/swagger_ui.py", line 110, in _get_openapi_json
    content=jsonifier.dumps(self._spec_for_prefix(request)),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/pyenv/lib/python3.12/site-packages/connexion/jsonifier.py", line 84, in dumps
    return self.json.dumps(data, **kwargs) + "\n"
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/json/encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
ValueError: Circular reference detected
INFO:     127.0.0.1:51944 - "GET /api/v1/swagger.json HTTP/1.1" 500 Internal Server Error

Looking through the changes between 3.2.0 and 3.3.0, I found that #2089 was the culprit, specifically the change in swagger_ui.py. I found that if I changed that back by changing my monkeypatches like this, then things were working again:

from connexion.spec import Specification
from connexion.middleware.swagger_ui import SwaggerUIAPI

print(f"Connexion version: {metadata.version('connexion')}")
if int(metadata.version("connexion").split(".")[1]) < 3:
    print("Applying monkeypatch to work around https://github.com/spec-first/connexion/issues/2028")
    Specification.clone = lambda self: type(self)(copy.deepcopy(self._raw_spec))
else:
    print("Applying monkeypatch to work around https://github.com/spec-first/connexion/issues/new_issue")
    SwaggerUIAPI._spec_for_prefix = lambda self, request: self.specification.with_base_path(self._base_path_for_prefix(request)).raw

And here's the resulting application log:

$ python -m uvicorn simple_app:app
Connexion version: 3.3.0
Applying monkeypatch to work around https://github.com/spec-first/connexion/issues/new_issue
INFO:     Started server process [3598725]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:39704 - "GET /api/v1/swagger.json HTTP/1.1" 200 OK

and the curl response:

$ curl -i http://localhost:8000/api/v1/swagger.json
HTTP/1.1 200 OK
date: Mon, 27 Oct 2025 12:49:15 GMT
server: uvicorn
content-length: 715
content-type: application/json

{"basePath": "/api/v1", "swagger": "2.0", "info": {"version": "1.0.0", "title": "Simple API"}, "definitions": {"widget": {"type": "object", "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "owners": {"type": "array", "items": {"$ref": "#/definitions/owner"}}}}, "owner": {"type": "object", "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "widgets": {"type": "array", "items": {"$ref": "#/definitions/widget"}}}}}, "paths": {"/owners": {"get": {"description": "Return a list of all registered owners", "operationId": "simple_app.get_owners", "responses": {"200": {"description": "A list of owners", "schema": {"type": "array", "items": {"$ref": "#/definitions/owner"}}}}}}}}
$

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions