redocly-cli icon indicating copy to clipboard operation
redocly-cli copied to clipboard

$ref lines not resolving during bundle - is there a limit to the number of $ref lines you can use in the bundle function?

Open nickcorby opened this issue 2 months ago • 11 comments

Describe the bug

I am using the bundle function to construct an Open API yaml file and am getting stuck with a single $ref line not resolving correctly & being updated to correct syntax in the bundled output file, no matter what I try. It seems that for whichever endpoint comes second in paths-base.yaml, one of the $ref lines just won't resolve. The bundled output contains the following:

If you swap the order of the endpoints being declared, this issue will swap to the other endpoint.

My only thought is that there is a limit to the number of $ref references the bundle function can handle?

To Reproduce Steps to reproduce the behavior:

  1. Import attached repo
  2. Run command redocly bundle base.yaml --output bundled-api.yaml
  3. See line 224:
                $ref: ../../components/models/MatterListResponseNew.yaml

Expected behavior

The $ref should resolve like

                $ref: '#/components/schemas/PublicDocsErrorResponse'

This only happens this this $ref, however if you swap the order of the endpoints in paths-base.yaml, this resolve issue will swap to the other endpoint.

Change:

/api/v1/cards:
  $ref: ../paths/endpoints/GET-api-v1-cards.yaml
/api/v3/matters:
  $ref: ../paths/endpoints/GET-api-v3-matters.yaml

to:

/api/v3/matters:
  $ref: ../paths/endpoints/GET-api-v3-matters.yaml
/api/v1/cards:
  $ref: ../paths/endpoints/GET-api-v1-cards.yaml

Logs

PS C:\repositories\openapi-spec-sanitized-fixed> redocly bundle base.yaml --output bundled-api.yaml
bundling base.yaml...
📦 Created a bundle for base.yaml at bundled-api.yaml 32ms.

This is on the latest version of redocly cli.

Please let me know if you require any additional details.

nickcorby avatar Oct 07 '25 07:10 nickcorby

You should start with fixing https://github.com/nickcorby/redocly-bundle-ref-issue/blob/main/paths/endpoints/GET-api-v1-cards.yaml#L35

This is invalid OpenAPI because the $ref overrides the response object with the reference to your shared assets and ignores the 200 definition entirely.

Image

I understand you're use of common schemas, but in this case, the OpenAPI Spec requirements are a bit verbose here. You need to define all of the response codes or use an extension x-. The Response Object is not a JSON Schema and doesn't support $ref alongside other keywords.

Image

jeremyfiel avatar Oct 07 '25 15:10 jeremyfiel

Hi @jeremyfiel thanks for taking the time to look at this and provide some feedback.

I know what I've written isn't valid OpenAPI on its own, but with the bundle function resolving all of the $ref lines, the output should be (unless I'm missing something). I'm not having any issues using the $ref within the responses object, as you can see in the code I've provided:

  responses:
    "200":
      description: "200 response"
      headers:
        Access-Control-Allow-Origin:
          schema:
            type: "string"
      content:
        application/json:
          schema:
            $ref: ../../components/models/MatterListResponseNew.yaml
    $ref: ../shared-assets/error-responses.yaml

becomes

      responses:
        '200':
          description: 200 response
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: ../../components/models/MatterListResponseNew.yaml
        '400':
          description: 400 response
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicDocsErrorResponse'

For whatever reason, $ref: ../shared-assets/error-responses.yaml is resolving correctly, the $ref lines within error-responses.yaml are also resolving, but $ref: ../../components/models/MatterListResponseNew.yaml isn't. For the other endpoint, it resolves just fine:

      responses:
        '200':
          description: 200 response
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardListJson'
        '400':
          description: 400 response
          headers:
            Access-Control-Allow-Origin:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicDocsErrorResponse'

This doesn't seem to be related to any specific $ref line or model or similar - the syntax is identical for all endpoints & swapping the order of the endpoints will resolve the error in one endpoint and move it to another. In my mind, this points to an issue with redocly.

The solution may just be that I need to list all of the responses in each endpoint - I was going for DRY (we will be adding hundreds of endpoints) but it may not be worth it if it's causing these issues.

nickcorby avatar Oct 07 '25 21:10 nickcorby

Thanks for reporting the issue, @nickcorby. We'll look into it when we have time. However, I'd still recommend you use the actual specification requirements on where $refs can be put. If you want our tools to help you to spot wrong ones, you can switch on the built-in spec-strict-refs rule. Moreover, it's generally a bad idea to use $refs alongside other fields (even though it's partially allowed) because it creates ambiguity regarding the inheritance.

Note: potentially related to https://github.com/Redocly/redocly-cli/issues/1907.

tatomyr avatar Oct 08 '25 08:10 tatomyr

Hi @tatomyr I'm not sure if this is related to the initial issue -

I'm restructuring my base.yaml file to try and make everything more OpenAPI compliant & am consulting the openapi-starter repo for guidance.

I've noticed in the readme file for Paths (https://github.com/Redocly/openapi-starter/blob/main/openapi/paths/README.md) that breaking endpoints up by operation underneath the path is mentioned as a viable option:

Image

However, when I adopt this structure in my base.yaml file, I get openAPI errors:

Code:

paths:
  /api/v1/cards:
    get:
      $ref: endpoints/api-v1-cards/GET.yaml
    post:
      $ref: endpoints/api-v1-cards/POST.yaml

Errors:

Image

Are these errors to be expected with this approach? Should I not not use this approach as recommended in the readme?

It's worth noting that the bundle function executes without issue, all $ref lines are resolved, etc. so there doesn't seem to be any actual issue.

I like this approach as it allows you to separate operations into individual files which I think is a lot cleaner than having them all in one, however having to live with these errors would be quite annoying.

nickcorby avatar Oct 23 '25 05:10 nickcorby

@nickcorby here's the excerpt of the starter docs:

Only the "file-per-path" option is semantically correct with the OpenAPI Specification 3.0.2. However, Redocly's openapi-cli will build valid bundles for any of the other options too.

We do try to bundle APIs correctly regardless $refs are used following the spec or not. However, in your case it seems that something went wrong, and I'm not sure what yet.

BTW, if you are restructuring your code, you can consider using the split command which creates a structure that we generally recommend to maintain.

I'll try to take a closer look at your example a bit later.

tatomyr avatar Oct 23 '25 07:10 tatomyr

@tatomyr thanks for clarifying - I guess the errors just come with the "filter-per-operation" option not being semantically correct.

Nice suggestion with the split command (thank you again). I tried it and it opted for the "filter-per-path" option, unfortunately! I was able to hack a "filter-per-operation" approach which is bundling correctly (and doesn't return any IDE errors):

base.yaml:

paths:
  /api/v1/cards:
    $ref: endpoints/api-v1-cards/base.yaml

endpoints/api-v1-cards/base.yaml:

get:
  $ref: GET.yaml
post:
  $ref: POST.yaml

endpoints/api-v1-cards/GET.yaml:

operationId: "Card_GetCards"
tags:
- Card
summary: "Gets a list of cards of a given firm.\r\nThe result only contains cards whose version is greater than lastRowVer."
parameters:
...
...
...

I'm not sure if this is technically "supported", but it does seem to work semantically, and it bundles without any issues that I can see.

Regarding the reported error, I haven't run into it since I cleaned up my $ref lines - they're all semantically correct now and I'm not referencing anything alongside other content within a property like this:

  responses:
    "200":
      description: "200 response"
      headers:
        Access-Control-Allow-Origin:
          schema:
            type: "string"
      content:
        application/json:
          schema:
            $ref: ../../components/models/MatterListResponseNew.yaml
    $ref: ../shared-assets/error-responses.yaml

I don't know if this is relevant or not - I can see a world in which my non-semantic references just broke something somewhere & it's technically a "bug" but may not be worth sinking that much time into.

nickcorby avatar Oct 23 '25 07:10 nickcorby

@nickcorby Thanks for the active discussion. We will definitely review and improve our openapi-starter. Please feel free to ask any questions about product and functionality.

AlbinaBlazhko17 avatar Oct 23 '25 08:10 AlbinaBlazhko17

it opted for the "filter-per-path" option, unfortunately!

@nickcorby do you suggest that the per-path references still produce an error? I think the issue might stem from declaring the components section explicitly. Normally, you don't need to list your components, as they are referenced within the paths section and automatically resolved by the bundler itself.

tatomyr avatar Oct 23 '25 12:10 tatomyr

@tatomyr no error from the per-path references or the split, I was just hoping for a per-operation split!

I'm not declaring my schemas property explicitly anymore which is working as you mentioned (being resolved & auto-added by the bundler), however I still need to declare the securitySchemas property within components, unless there's another way to do this?

nickcorby avatar Oct 23 '25 20:10 nickcorby

i took another stab at this one and fixed up some issues. https://github.com/jeremyfiel/redocly-bundle-ref-issue

Now I only see one issue with the bundled.yaml file which is the same issue you posted about, the second endpoint doesn't resolve the response body correctly.

If i remove the $ref: /shared-assests/error-responses.yaml definition from the second endpoint, the bundle is successfully processed.

The interesting part is it seems to work correctly if you remove that definition from the second entry of paths-base.yaml. Even with the first entry and the error-responses.yaml definition still intact, the bundle is successful.

I would say this is a bug, but honestly, it's against the spec to use $ref here, so it's highly discouraged and it's been well documented in the newer updates to the spec from 3.0.3 onwards; fixing this issue shouldn't be a priority.

jeremyfiel avatar Oct 23 '25 21:10 jeremyfiel

however I still need to declare the securitySchemas property within components

@nickcorby yes, you can declare securitySchemas in components, it shouldn't interfere with schemas.

tatomyr avatar Oct 31 '25 14:10 tatomyr