Skip to content

fix: prevent nil embedded Reference panic on sibling operations when a $ref fails to build (#616) - #623

Open
rifkir23 wants to merge 1 commit into
pb33f:mainfrom
rifkir23:fix/pathitem-nil-reference-panic
Open

fix: prevent nil embedded Reference panic on sibling operations when a $ref fails to build (#616)#623
rifkir23 wants to merge 1 commit into
pb33f:mainfrom
rifkir23:fix/pathitem-nil-reference-panic

Conversation

@rifkir23

@rifkir23 rifkir23 commented Sep 3, 2026

Copy link
Copy Markdown

Problem

When one operation in a path item fails to build (for example, an operation whose response schema is a dangling $ref), BuildV3Model still returns a model along with the error. The sibling operations in that same path item are returned with a nil embedded *low.Reference, so calling IsReference() on them panics:

panic: runtime error: invalid memory address or nil pointer dereference

A single bad $ref can therefore take down anything that walks the model. This is hit in practice through daveshanley/vacuum (which walks the model with pb33f/doctor).

Fixes #616

Reproduction

Two operations in one path item, only get has the dangling $ref:

openapi: 3.0.2
info: { title: t, version: 1.0.0 }
paths:
  /p:
    get:
      responses:
        "200":
          description: ok
          content:
            application/json:
              schema: { $ref: "#/components/schemas/Nope" }
    post:
      responses:
        "200": { description: ok }
model, _ := doc.BuildV3Model()
pi, _ := model.Model.Paths.PathItems.Get("/p")
pi.Post.GoLow().IsReference() // panic: nil pointer dereference

It needs two or more operations in the same path item; a single-operation path item does not reproduce it.

Root cause

Operation embeds *low.Reference (a pointer), and that embedded pointer is only initialized inside Operation.Build():

o.reference = low.Reference{}
o.Reference = &o.reference

In PathItem.Build, operations are built via TranslateSliceParallel. When one operation returns an error, the translate loop stops, so sibling operations that were constructed with low.BuildModel (which intentionally skips embedded/anonymous fields) but never reached Build() keep a nil embedded *low.Reference. IsReference() has a value receiver, so invoking it through the nil embedded pointer dereferences nil and panics.

Fix

Initialize the embedded reference at construction time, right after low.BuildModel, for both standard and additional operations. The operation is then always safe to inspect, even if a sibling operation fails to build and this one is never built. This does not change the reported $ref error, only the state of the model returned alongside it.

Tests

Added TestPathItem_Build_SiblingOperationSafeAfterRefError (low/v3): builds a path item with a dangling $ref on one operation, asserts the build surfaces the error, and asserts the sibling operation is non-nil and IsReference() does not panic. Verified it FAILS with a nil pointer dereference without the fix and PASSES with it. Full datamodel/low/v3 and datamodel/high/v3 suites pass; gofmt and go vet clean.

@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.75%. Comparing base (ca6ca73) to head (674c215).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #623      +/-   ##
==========================================
- Coverage   99.78%   99.75%   -0.03%     
==========================================
  Files         283      283              
  Lines       34456    34458       +2     
==========================================
- Hits        34382    34374       -8     
- Misses         46       55       +9     
- Partials       28       29       +1     
Flag Coverage Δ
unittests 99.75% <100.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BuildV3Model returns a partially-built model that panics on public API

2 participants