@@ -304,13 +304,22 @@ func (s *JSONSchema[Referenceable]) resolve(ctx context.Context, opts references
304304 localBaseURI = jsID
305305 }
306306 }
307- // Get the ref to build absolute reference with fragment
308- jsRef := js .GetRef ()
309- absRef := utils .BuildAbsoluteReference (localBaseURI , string (jsRef .GetJSONPointer ()))
310- js .referenceResolutionCache = & references.ResolveResult [JSONSchemaReferenceable ]{
311- AbsoluteDocumentPath : localBaseURI ,
312- AbsoluteReference : references .Reference (absRef ),
313- ResolvedDocument : result .ResolvedDocument ,
307+
308+ // Only pre-populate the cache if it hasn't already been fully resolved.
309+ // A nested ref may already have a complete resolution from a previous walk
310+ // (e.g., when multiple references point to the same external document).
311+ // Overwriting a correct cache with a pre-populated one causes false circular
312+ // reference detection because the pre-populated AbsoluteReference uses the
313+ // parent document path rather than the resolved child path.
314+ if js .referenceResolutionCache == nil || js .referenceResolutionCache .Object == nil {
315+ // Get the ref to build absolute reference with fragment
316+ jsRef := js .GetRef ()
317+ absRef := utils .BuildAbsoluteReference (localBaseURI , string (jsRef .GetJSONPointer ()))
318+ js .referenceResolutionCache = & references.ResolveResult [JSONSchemaReferenceable ]{
319+ AbsoluteDocumentPath : localBaseURI ,
320+ AbsoluteReference : references .Reference (absRef ),
321+ ResolvedDocument : result .ResolvedDocument ,
322+ }
314323 }
315324
316325 // Collect this reference for setting parent links after the walk
0 commit comments