Summary
Compiling a document whose schema graph contains a reference cycle with no concrete node — a schema that $refs itself, a $ref cycle A → B → A, or a self-referential YAML anchor — sends the loader into unbounded recursion and the process dies with fatal error: stack overflow (exit 2). Because a stack overflow is a Go runtime fatal error rather than a recoverable panic, the loader's recover cannot trap it, so the crash escapes to the caller.
Legitimate recursive schemas (a property that $refs its enclosing schema, e.g. a linked-list Node) compile correctly — only degenerate cycles that never reach a concrete schema node crash.
A related, less severe case in the same area: a whitespace-only document makes the parser panic with unknown node kind. That one is a recoverable panic and is straightforward to trap; it is included here because it belongs to the same "loader must survive degenerate input" concern.
Reproduction
Each of these crashes the compiler with fatal error: stack overflow:
Self-reference:
openapi: 3.1.0
info: {title: t, version: '1'}
paths: {}
components:
schemas:
A:
$ref: '#/components/schemas/A'
Recursive YAML anchor:
openapi: 3.1.0
info: {title: t, version: '1'}
paths: {}
x-a: &a [*a]
Root cause
Reference resolution (compilers/openapi/load.go, around the ResolveAllReferences call) and the underlying YAML/reference walk traverse the reference/anchor graph without a visited-set or depth bound, so a cycle recurses until the goroutine stack is exhausted. The loader's recover only wraps the initial unmarshal, and a fatal stack overflow is not recoverable in any case.
Expected
The compiler must never crash the process on any parseable input. A reference or anchor cycle should be detected before or during resolution and reported as an unresolved/cyclic-reference diagnostic, leaving the rest of the pipeline to continue.
Summary
Compiling a document whose schema graph contains a reference cycle with no concrete node — a schema that
$refs itself, a$refcycleA → B → A, or a self-referential YAML anchor — sends the loader into unbounded recursion and the process dies withfatal error: stack overflow(exit 2). Because a stack overflow is a Go runtime fatal error rather than a recoverable panic, the loader'srecovercannot trap it, so the crash escapes to the caller.Legitimate recursive schemas (a property that
$refs its enclosing schema, e.g. a linked-listNode) compile correctly — only degenerate cycles that never reach a concrete schema node crash.A related, less severe case in the same area: a whitespace-only document makes the parser panic with
unknown node kind. That one is a recoverable panic and is straightforward to trap; it is included here because it belongs to the same "loader must survive degenerate input" concern.Reproduction
Each of these crashes the compiler with
fatal error: stack overflow:Self-reference:
Recursive YAML anchor:
Root cause
Reference resolution (
compilers/openapi/load.go, around theResolveAllReferencescall) and the underlying YAML/reference walk traverse the reference/anchor graph without a visited-set or depth bound, so a cycle recurses until the goroutine stack is exhausted. The loader'srecoveronly wraps the initial unmarshal, and a fatal stack overflow is not recoverable in any case.Expected
The compiler must never crash the process on any parseable input. A reference or anchor cycle should be detected before or during resolution and reported as an unresolved/cyclic-reference diagnostic, leaving the rest of the pipeline to continue.