Skip to content

Error starting a tunnel: "RangeError: Maximum call stack size exceeded" #34

@kolessios

Description

@kolessios

When trying to run the following code:

const { Tunnel } = await import('cloudflared')

const tunnel = Tunnel.quick('http://localhost:8000')
const url = await new Promise<string>(resolve => tunnel.on('url', resolve))

I encountered this error:

RangeError: Maximum call stack size exceeded
  at Tunnel.emit (node:events:465:44)
  at Tunnel.emit (.../node_modules/cloudflared/lib/tunnel.js:112:18)
  at Tunnel.<anonymous> (.../node_modules/cloudflared/lib/tunnel.js:78:12)
  at Tunnel.emit (node:events:530:35)
  at Tunnel.emit (.../node_modules/cloudflared/lib/tunnel.js:112:18)
  at Tunnel.<anonymous> (.../node_modules/cloudflared/lib/tunnel.js:78:12)
  at Tunnel.emit (node:events:530:35)
  at Tunnel.emit (.../node_modules/cloudflared/lib/tunnel.js:112:18)
  at Tunnel.<anonymous> (.../node_modules/cloudflared/lib/tunnel.js:78:12)
  at Tunnel.emit (node:events:530:35)

After some investigation, I found that this error originates from this block of code:

private setupEventHandlers() {
// cloudflared outputs to stderr, but I think its better to listen to stdout too
this.on("stdout", (output) => {
this.processOutput(output);
}).on("error", (err) => {
this.emit("error", err);
});
this.on("stderr", (output) => {
this.processOutput(output);
}).on("error", (err) => {
this.emit("error", err);
});
}

Specifically, the issue arises because inside .on('error'), it calls .emit("error", err) which causes an infinite loop by repeatedly listening to and emitting the same event.


After removing this.emit("error", err); and replacing it with console.error(err); I discovered that the original error is caused by the cloudflared binary not being found:

Error: spawn .../node_modules/cloudflared/bin/cloudflared ENOENT
    at Process.ChildProcess._handle.onexit (node:internal/child_process:285:19)
    at onErrorNT (node:internal/child_process:483:16)
    at processTicksAndRejections (node:internal/process/task_queues:90:21) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'spawn .../node_modules/cloudflared/bin/cloudflared',
  path: '.../node_modules/cloudflared/bin/cloudflared',
  spawnargs: [ 'tunnel', '--url', 'http://localhost:8000' ]
}

To avoid confusion, I suggest updating the documentation and the example script to include the use of install.

In my specific case, this is the final code without the error:

const fs = await import('fs')
const { Tunnel, bin, install } = await import('cloudflared')

if (!fs.existsSync(bin)) {
  // install cloudflared binary
  await install(bin)
}

const tunnel = Tunnel.quick('http://localhost:8000')
const url = await new Promise<string>(resolve => tunnel.on('url', resolve))

Thank you for the library. 😊
Related issue: #30

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions