Search code examples
javascriptnode.jsnpmserverless-stack

Serverless stack: Maximum call stack size exceeded only in windows during npm run start


I started the guide for serverless stack, and I hit a wall pretty soon in Windows 10 (the same steps don't cause any issue in Mac or Linux).

I created a simple serverless stack project, and doing: npm run start

I got this:

RangeError: Maximum call stack size exceeded at Object.resolve (path.js:153:10) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:38) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12) at getManager (D:\Sources\demo-notes-app\node_modules@serverless-stack\core\dist\packager\packager.js:48:12)

Node version is v14.17.5, npm is 7.21.1

Since I barely instantiated the project and it is working on other platforms, I really don't understand how to troubleshoot this.

Thanks


Solution

  • Well, you would typically start by examining the code/files in the call stack and see if that gives you any clues. It appears from the little we can see there that getManager() is calling itself over and over again. Just guessing, but perhaps this is a configuration error or some other error path that triggers it to do this.

    If you go look at serverless-stack/core/dist/packager/packager.js on github, you see this function:

    export function getManager(dir: string): Manager {
      const lock = path.join(dir, "yarn.lock");
      if (fs.existsSync(lock)) return Yarn;
      if (dir === "/") return NPM;
      return getManager(path.resolve(dir, ".."));
    }
    

    So, what it appears this is attempting to do is to look for a yarn.lock file. If one is found, return Yarn. If we're already at the top level path of /, then return NPM. Otherwise, call itself recursively, but go up one level in the directory hierarchy and start all over. So, if the code was actually working that way, it should end up with dir === "/" at the top level on *nix, except that might not be the case on Windows for two possible reasons:

    1. Windows may have a drive letter at the start of the path.
    2. Windows uses "\" as the path delimiter, not "/".

    One way you can narrow this down is to temporarily put some logging in this getManager() function to see what it is being passed (just temporarily edit the version in your dist directory - make sure you're editing the compiled/transpiled version, not the TypeScript version unless you're going to build it again).

    export function getManager(dir: string): Manager {
      console.log("getManager()", dir);            // <== add this
      const lock = path.join(dir, "yarn.lock");
      if (fs.existsSync(lock)) return Yarn;
      if (dir === "/") return NPM;
      return getManager(path.resolve(dir, ".."));
    }
    

    Seeing what is being passed here will indicate which of the above two problems may be causing the problem. Judging by the code, it could probably be made to work by just making sure you only pass it a path with forward slashes, but that would have to be tested to see if the other code involved here is OK with that.

    FYI, this code could be made more tolerant of the Windows world by changing the code to this:

    // regex that matches / or \ or d:/ or d:\
    const topPath = /^(\/|\\|[a-z]:[\/\\]|)$/i;
    
    export function getManager(dir: string): Manager {
      console.log("getManager()", dir);
      const lock = path.join(dir, "yarn.lock");
      if (fs.existsSync(lock)) return Yarn;
      if (topPath.test(dir)) return NPM;
      return getManager(path.resolve(dir, ".."));
    }
    

    Note, this has not been coded to handle UNC paths on Windows which start with "\\", but it certainly could be adapted for that too.


    In thinking about it a little more, here's a simpler way to avoid the infinite loop when you get to the top. If trying to go up .. doesn't change the path, then you're at the top:

    export function getManager(dir: string): Manager {
      const lock = path.join(dir, "yarn.lock");
      if (fs.existsSync(lock)) return Yarn;
      let upDir = path.resolve(dir, "..");
      // if we didn't actually go up any more, then we're at the top
      if (upDir === dir) {
          return NPM;
      }
      return getManager(upDir);
    }