Skip to content

Unclear error validating params with circular references #288

Description

@litteratum

Similar to #287, but now it is about parameters' validation.

Given a spec:

openapi: 3.1.0
info:
  title: Minimal Recursion
  version: 1.0.0
paths:
  /objects:
    get:
      parameters:
        - name: filter
          in: query
          schema:
            $ref: '#/components/schemas/Node'
      responses:
        "200":
          description: ok
components:
  schemas:
    Node:
      type: object
      properties:
        child:
          $ref: '#/components/schemas/Node'

Attempt to validate a request leads to:

Message: Query parameter 'filter' failed schema compilation
Reason : The query parameter 'filter' schema compilation failed: failed to unmarshal JSON schema: EOF

Problems

  1. The error is unclear
  2. I expected circular references to be supported. Already reported in Are circular references unsupported? #287

How to reproduce

I have the following versions:

  • github.com/pb33f/libopenapi-validator v0.13.10
  • github.com/pb33f/libopenapi v0.38.1
  • Go 1.26.2

Here is a code snippet:

package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"

	"github.com/pb33f/libopenapi"
	validator "github.com/pb33f/libopenapi-validator"
)

const specYAML = `openapi: 3.1.0
info:
  title: Minimal Recursion
  version: 1.0.0
paths:
  /objects:
    get:
      parameters:
        - name: filter
          in: query
          schema:
            $ref: '#/components/schemas/Node'
      responses:
        "200":
          description: ok
components:
  schemas:
    Node:
      type: object
      properties:
        child:
          $ref: '#/components/schemas/Node'
`

func main() {
	if err := run(); err != nil {
		fmt.Fprintf(os.Stderr, "reproduction setup failed: %v\n", err)
		os.Exit(2)
	}
}

func run() error {
	// 1. Parse the document and build the v3 model.
	document, err := libopenapi.NewDocument([]byte(specYAML))
	if err != nil {
		return fmt.Errorf("failed to parse OpenAPI spec: %w", err)
	}
	if _, mErr := document.BuildV3Model(); mErr != nil {
		return fmt.Errorf("failed to build OpenAPI v3 model: %w", mErr)
	}

	// 2. Create the request validator.
	v, errs := validator.NewValidator(document)
	if len(errs) > 0 {
		return fmt.Errorf("failed to create validator: %v", errs)
	}

	// 3. Validate a request that exercises the recursive query-parameter schema.
	req := httptest.NewRequest(http.MethodGet, "http://example.com/objects?filter=x", nil)

	ok, valErrs := v.ValidateHttpRequest(req)

	fmt.Printf("ValidateHttpRequest ok = %v\n", ok)
	fmt.Printf("validation errors  = %d\n", len(valErrs))
	for i, ve := range valErrs {
		fmt.Printf("\n--- error %d ---\n", i)
		fmt.Printf("Message: %s\n", ve.Message)
		fmt.Printf("Reason : %s\n", ve.Reason)
	}
	return nil
}

Some digging with AI

In parameters/validate_parameter.go, ValidateParameterSchema renders the parameter
schema inline and discards the render error.
For a circular schema, RenderInlineWithContext cannot produce a finite inline document,
so renderedSchema comes back empty. The empty bytes flow into the compiler.

So there are two defects:

  1. Correctness — the parameter path inline-renders the schema, which is impossible
    for recursive $refs. It should validate against the schema with $refs preserved
    (resolved by the compiler), the way the request-body path does.
  2. Diagnostics — the render error from RenderInlineWithContext is discarded
    (_), and there's no empty-input guard before jsonschema.UnmarshalJSON, so the user
    gets EOF instead of a message naming the circular reference.

Suggested fix

In ValidateParameterSchema / ValidateSingleParameterSchema, stop relying on inline
rendering for schemas that contain circular references — compile and validate against the
schema with $refs intact (as the body path / RenderSchemaForValidation does). At a
minimum: do not discard the RenderInlineWithContext error, and guard against empty
rendered output before calling NewCompiledSchema, returning a circular-reference error
instead of EOF.

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