Skip to content

Ruby 3 upgrade throws kernel error for the overwritten each method #232

Open

Description

@kajatiger

🐞 Bug Report

Steps to reproduce

We have an item that is the first hyperlink to the actual media and need to iterate through the sub nodes of it:

item.sub_nodes.map(&:name).to_s

And then we get an error saying

SystemStackError:
        stack level too deep

since upgrading the rails app to ruby 3 (and rails 6.1)

Expected behavior

We would expect to get a string back that contains the name of each item sub node.

Possible Solution

We don't exactly have a solution, but suppose the answer would be somewhere in the definition of the each block here:

def each(&block)

Activity

  1. dblock commented on Jan 12, 2022

    @dblock
    Collaborator

    Start by adding Ruby 3 to https://github.com/codegram/hyperclient/blob/master/.travis.yml, do we have a repro of this out of the box with existing specs? If not, try building a repro?

  2. kajatiger commented on Jan 13, 2022

    @kajatiger
    Author

    We fixed this in one spot now with adding embedded:
    https://github.com/artsy/impulse/pull/805#discussion_r783322301

  3. dblock commented on Jan 13, 2022

    @dblock
    Collaborator

    We fixed this in one spot now with adding embedded: https://github.com/artsy/impulse/pull/805#discussion_r783322301

    Looks like impulse is a private repo @kajatiger.

  4. dblock commented on Feb 16, 2023

    @dblock
    Collaborator

    I fixed CI/CD with GHA on this repo and added Ruby 3.2 without issues. Interested in taking this on @kajatiger?

  5. dblock commented on Aug 26, 2026

    @dblock
    Collaborator

    👋 I'm an AI assistant, commenting on behalf of @dblock.

    We were able to reproduce this SystemStackError locally. The root cause is in Link#each (link.rb#L28):

    def each(&block)
      if block_given?
        current = self
        while current
          coll = current.respond_to?(@key) ? current.send(@key) : _resource
          coll.each(&block)
          break unless current._links[:next]
    
          current = current._links.next
        end
      else
        to_enum(:each)
      end
    end

    If current.send(@key) (via method_missing/delegation) resolves to something that ultimately calls back into the same Link#each again — for example, when a link's key name (like sub_nodes) matches a link/embedded name on the resource it points to, forming a cycle — coll.each(&block) recurses forever, eventually blowing the stack with SystemStackError: stack level too deep instead of a clear error.

    We confirmed this with a minimal repro: a resource whose sub_nodes link points to another resource that itself links sub_nodes back to the same URL, so calling .each (or anything that delegates through each, like .map) recurses infinitely.

    Two possible fixes, would like the maintainers' input on preferred approach:

    1. Cycle detection — track visited resources/URLs during the each/pagination walk and raise a clear Hyperclient::CircularLinkError (or similar) instead of silently blowing the stack.
    2. Iteration cap — a simpler, more generic safety valve that raises after some large number of iterations, without needing to track visited state.

    Happy to submit a PR for either approach — let us know which you'd prefer, or if there's a different angle you'd like us to take.

    cc @kajatiger — if you have a more specific real-world repro, it'd help confirm we're fixing the right scenario.

  6. kajatiger commented on Aug 27, 2026

    @kajatiger
    Author

    No sorry, I have not looked into this problem in 3-4 years and am working in a different codebase now 🤪

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions