Skip to content

Swagger UI endpoint crashes at runtime with JSON Schema keywords like propertyNames or patternProperties #2078

@giuliohome

Description

@giuliohome

Description

Connexion fully supports OpenAPI 3.x and injects features like propertyNames or patternProperties into the generated schema at runtime. These are valid under JSON Schema (especially in OpenAPI 3.1), and Connexion accepts and uses them.

However, Connexion fails at runtime when serving the Swagger UI.
This causes errors like:

must NOT have additional properties
must match exactly one schema in oneOf

Concerns:

  • Users expect their runtime schema and documentation schema to align.

Expected Behavior

Connexion should allow serving Swagger UI for OpenAPI specs containing modern JSON Schema keywords without crashing.

At minimum, it could provide a configurable option to sanitize the spec automatically for the documentation endpoint.

Steps to Reproduce

  1. Create an OpenAPI 3.x spec containing propertyNames or patternProperties.
  2. Load it in Connexion with Swagger UI enabled.
  3. Access /docs/.
  4. Observe runtime error preventing the Swagger UI from loading.

Environment

  • Connexion version: connexion[flask,uvicorn,swagger-ui]==3.2.0
  • Python version: 3.12.2

Our workaround

def patch_openapi_spec(schema):
    """
    Patch OpenAPI spec to replace problematic JSON Schema keywords
    with x-prefixed versions for documentation compatibility
    """
    if isinstance(schema, dict):
        if "patternProperties" in schema:
            schema["x-patternProperties"] = schema.pop("patternProperties")
        if "propertyNames" in schema:
            schema["x-propertyNames"] = schema.pop("propertyNames")
        for v in schema.values():
            patch_openapi_spec(v)
    elif isinstance(schema, list):
        for v in schema:
            patch_openapi_spec(v)

def generate_config_doc_yaml():
    """
    Generate documentation-safe YAML from validation YAML
    """
    validate_config_path = os.path.join(os.path.dirname(__file__), 'openapi', 'config-api.yaml')
    doc_config_path = os.path.join(os.path.dirname(__file__), 'openapi', 'config-api-doc.yaml')
    
    try:
        with open(validate_config_path, 'r', encoding='utf-8') as f:
            spec = yaml.safe_load(f)
        
        patch_openapi_spec(spec)
        
        with open(doc_config_path, 'w', encoding='utf-8') as f:
            yaml.dump(spec, f, sort_keys=False, allow_unicode=True)
            
        return doc_config_path
    except Exception as e:
        logging.error(f"Error generating config doc YAML: {e}")
        return None

app.add_api('config-api.yaml', name='config', arguments={'title': 'Configuration'}, strict_validation=True, swagger_ui=False)

# Custom documentation routes for config API
@app.route("/api/v1.0/configuration/docs/openapi.yaml")
def config_docs_spec():
    """Serve the documentation-safe OpenAPI spec for config API"""
    return send_from_directory(os.path.join(os.path.dirname(__file__), 'openapi'), 'config-api-doc.yaml')

@app.route("/api/v1.0/configuration/docs/")
def config_swagger_ui():
    """Serve custom Swagger UI for config API"""
    return f"""
    <!DOCTYPE html>
    <html>
    <head>
      <title>C3PO Configuration API Documentation</title>
      <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css" />
    </head>
    <body>
      <div id="swagger-ui"></div>
      <script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
      <script>
      const ui = SwaggerUIBundle({{
        url: '/api/v1.0/configuration/docs/openapi.yaml',
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIBundle.presets.standalone
        ]
      }});
      </script>
    </body>
    </html>
    """

Maybe related

#1396
#2043
https://editor-next.swagger.io/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions