Skip to content

Volta extends resolution in getNodeVersionFromFile has no cycle detection, recursing until stack overflow #1628

Open

Description

@krishna3554

Summary

getNodeVersionFromFile follows Volta extends recursively with no cycle detection, so a self-referential or mutually-referential volta.extends chain causes unbounded recursion and crashes the action with a stack overflow instead of a clear error.

Location

  • File: src/util.ts
  • Function: getNodeVersionFromFile(versionFilePath: string)
  • Code path:
if (manifest.volta?.extends) {
  const extendedFilePath = path.resolve(
    path.dirname(versionFilePath),
    manifest.volta.extends
  );
  core.info('Resolving node version from ' + extendedFilePath);
  return getNodeVersionFromFile(extendedFilePath);
}

No visited-path set or depth limit is threaded through the recursion.

Problem

Volta workspaces support {"volta": {"extends": "./base/package.json"}}. If the target (transitively) points back to a file already on the resolution stack — e.g. package.json with "extends": "./package.json" (typo), or a.json → b.json → a.json — the function recurses forever until V8 throws RangeError: Maximum call stack size exceeded. The user gets an opaque stack-overflow failure rather than a diagnostic naming the cycle.

Trigger / Reproduction

Based on static analysis (no workflow run performed):

  1. Set node-version-file to a package.json containing:
    {"volta": {"extends": "./package.json"}}
    or a two-file cycle a.json ↔ b.json via volta.extends.
  2. Run the action. getNodeVersionFromFile resolves extends, re-enters itself with the same path, and never terminates normally.

Note: this is a static-analysis finding; I did not execute a workflow against a cyclic fixture.

Expected Behavior

Cyclic volta.extends should fail fast with a clear error naming the files in the cycle (e.g. Detected cyclic volta.extends: a.json -> b.json -> a.json), consistent with how missing files already throw The specified node version file at: ... does not exist.

Actual Behavior

Unbounded recursion until stack exhaustion, producing an unactionable RangeError with no mention of the offending extends chain.

Impact

  • A one-character typo in volta.extends turns a configuration mistake into an opaque action crash, costing debugging time.
  • No data-loss risk, but the failure mode hides the actual cause (cycle) behind a generic engine error.

Suggested Direction

  • Thread a seen: Set<string> (resolved absolute paths) through getNodeVersionFromFile, checking before recursing and throwing a descriptive cycle error. Alternatively cap recursion depth with the same diagnostic. Either preserves current behavior for acyclic chains.

Evidence

  • Source via API: src/util.ts Volta-extends branch shows direct unconditional recursion with no guard; neighboring branches (missing file, non-JSON/TOML fallthrough) all have explicit handling, highlighting the gap.
  • Duplicate check: issue search for volta extends cycle recursion returns total_count: 0, and the open-issue list contains no extends-cycle report (nearest is .nvmrc comment parsing) — no apparent duplicate.

What happened

Unbounded recursion on cyclic Volta extends as detailed above.

Expected behavior

Fast, descriptive cycle error instead of stack overflow.

Steps to reproduce

Point node-version-file at a self- or mutually-referential Volta extends fixture and observe getNodeVersionFromFile re-enter indefinitely (static path; runtime stack overflow implied).

reproducible code

// package.json
{"volta": {"extends": "./package.json"}}
- uses: actions/setup-node@v4
  with:
    node-version-file: package.json

manifest.yaml

N/A

Versions

  • actions/setup-node: current main (verified via API, src/util.ts)
  • Node: N/A (static analysis finding)

Classification

  • FACT: Volta extends recursion has no visited-set or depth cap (verified in source via API).
  • INFERENCE: cyclic fixtures therefore recurse until stack exhaustion.
  • HYPOTHESIS: a visited-path guard produces a clear error with no change to valid chains.

Activity

  1. v-priya-kinthali commented on Sep 11, 2026

    @v-priya-kinthali
    Contributor

    Hello @krishna3554👋,
    Thank you for reporting this issue. We will investigate it and get back to you as soon as we have some feedback.

  2. z0rimo commented on Sep 14, 2026

    @z0rimo

    I noticed this is assigned already. Are you actively working on a fix?
    If not, I'd be happy to take it and add regression coverage for self-referential and multi-file volta.extends cycles.

  3. v-gowridurgad commented on Sep 21, 2026

    @v-gowridurgad
    Contributor

    Hi @z0rimo We’re actively working on this and will share an update once we have progress to report.

  4. added a commit that references this issue on Oct 6, 2026
    5a178ee
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions