Skip to content

fix(bundler): inline a $ref to a sequence or scalar as that kind of node - #637

Merged
daveshanley merged 1 commit into
mainfrom
fix/composed-bundler-ref-to-sequence
Sep 25, 2026
Merged

daveshanley merged 1 commit into
mainfrom
fix/composed-bundler-ref-to-sequence

Conversation

@daveshanley

Copy link
Copy Markdown
Member

Fixes #607. Supersedes #608.

Problem

BundleBytesComposed panics with index out of range [N] with length N when a $ref resolves to an odd-length array, e.g. tags: {$ref: "tags.yaml#/tags"}.

walkAndRewriteRefs already handles sequences correctly. The corrupt node comes from the composer. When a $ref can't be lifted into components, the inline fallback (inlineProcessRef, inlineMatchingRefs, rewriteInlinedAbsoluteRefs) replaces only the $ref node's .Content with the target's .Content. The $ref node is a mapping, so a sequence target becomes a mapping node holding N sequence items:

  • odd N → the walker reads Content[i+1] past the end → panic;
  • even N → no panic, but a corrupted mapping is left in the index. This is why the issue saw 64 succeed and 63/65 fail.

Fix

One helper, inlineRefNode(refNode, target), used at all four Content-only sites:

  • mapping target: swaps Content only, exactly as today, so the ref node keeps its own style and comments. Output for the common case is unchanged.
  • any other target (sequence, scalar, alias): also adopts the target's Kind, Tag, Style, Value and Alias.

replaceRefNodeWithContent (extension refs) wasn't reused because it always copies style, anchor and comments. That would change the rendered output of ordinary mapping refs.

Why not #608

#608 adds a bounds check inside walkAndRewriteRefs. That stops the panic but leaves the corrupted mapping node in place, and once the node is built correctly the guard can never trigger.

Note: $ref'd tags are still not in the output

After this fix neither composed nor inline (BundleBytes) bundling crashes, but neither emits tags for tags: {$ref: ...}. This was already true for inline bundling on main. The bundlers render from the model, and extractTags (datamodel/low/v3/create_document.go) only accepts a sequence node. OpenAPI 3.x does not allow a Reference Object for tags, so the model ignores it. Supporting it would mean resolving the ref in extractTags, while keeping the original key/value nodes so tags still renders in its original position. That's a separate decision, so it's not in this PR.

Tests

bundler/issue607_test.go:

  • TestBundleBytesComposed_Issue607_SequenceRefDoesNotPanic: 1, 2, 3, 63, 64 and 65 tags. The odd counts panic on main.
  • TestBundleDocumentComposed_Issue607_InlinedSequenceRefBecomesSequence: after composing, the root tags node is a real !!seq with the three tags. An operation-level tags: {$ref: ...} flow list renders as [a, b, c].
  • TestInlineRefNode_Issue607: sequence, scalar, alias and mapping targets. The mapping case keeps its kind, tag, flow style and head comment.

bundler coverage: 100%. ./tests and the root package pass.

🤖 Generated with Claude Code

When composed bundling cannot lift a $ref into components (for example
`tags: {$ref: tags.yaml#/tags}`), the inline fallback replaced only the
$ref node's Content with the target's Content. The $ref node is a
mapping, so a sequence target produced a mapping node holding the
sequence items. walkAndRewriteRefs walks mappings in key/value pairs, so
an odd number of items read past the end of the slice and panicked with
"index out of range [N] with length N"; an even number silently left a
corrupted mapping behind.

All four Content-only replacement sites now go through inlineRefNode,
which swaps content for mapping targets exactly as before and, for any
other target, also adopts its kind, tag, style, value and alias.

Fixes #607

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (61ea631) to head (9fa9a3e).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #637   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          297       297           
  Lines        37662     37671    +9     
=========================================
+ Hits         37662     37671    +9     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

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.

@daveshanley
daveshanley merged commit 124548c into main Sep 25, 2026
8 checks passed
daveshanley added a commit that referenced this pull request Sep 25, 2026
…writeRefs

The bounds check added to walkAndRewriteRefs for #607 covered a mapping
node holding sequence content. That node came from the composer's
inline fallback copying only a sequence target's Content onto a $ref
mapping node, which is now fixed at the source (inlineRefNode, #637).
The walker can no longer be handed an odd-length mapping, so the guard
never runs and was the only uncovered block in the bundler package.

The regression test from #608 (63, 64 and 65 $ref'd tags through
BundleBytesComposed) stays and passes without the guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Composed bundler panics with "index out of range" when a $ref resolves to an odd-length array (e.g. root tags)

1 participant