node.jserror-handlingasync-awaitfsstack-trace

Stack trace with fs/promises and async..await


I have a problem that is similar to this one and described in this issue. There are no stack traces in errors that come from fs/promises when used with async..await, and no way to determine where the error happened.

Given the app:

foo.js

const { bar } = require('./bar');

async function foo() {
  await bar();
}


(async () => {
  await foo();
})().catch(console.error);

bar.js

const fs = require('fs/promises');

exports.bar = async function bar() {
  await fs.readFile('some');
}

When it runs like node foo.js, this results in an error with no stack trace:

[Error: ENOENT: no such file or directory, open '...\some'] {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: '...\\some'
}

Where error.stack contains 'ENOENT: no such file or directory, open '...\some'.

When it runs like node --stack_trace_limit=100 -r trace foo.js with trace package, like it's suggested in linked sources, this results in this stack trace:

stack trace

Notice that internal entries are grayed and can be filtered out.

This node --stack_trace_limit=100 -r trace -r clarify foo.js with trace and clarify packages results in this output:

Error: ENOENT: no such file or directory, open '...\some'
    at ...\foo.js:8:2
    at Object.<anonymous> (...\foo.js:10:3) {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: '...\\some',
  [Symbol(originalCallSite)]: [],
  [Symbol(mutatedCallSite)]: [ CallSite {}, CallSite {} ]
}

The problem is that bar.js isn't mentioned in all of the outputs.

My intention is to provide clean error output with no internal entries and the exact location where the error occurs, i.e. line number in bar.js.

What are possible solutions to this problem?


Solution

  • One possible solution is to use Node.js >= v21.2.0. Node.js release v21.2.0 included a commit from PR 49849 that adds stack traces to node:fs/promises.

    ES modules and top level await.

    package.json

    "type": "module"
    

    bar.js

    import { readFile } from 'node:fs/promises'
    
    export async function bar() {
      await readFile('some')
    }
    

    foo.js

    import { bar } from './bar.js'
    
    await bar()
    

    Now run node foo.js to generate the stack trace:

    node:internal/fs/promises:638
      return new FileHandle(await PromisePrototypeThen(
                            ^
    
    Error: ENOENT: no such file or directory, open 'some'
        at async open (node:internal/fs/promises:638:25)
        at async readFile (node:internal/fs/promises:1251:14)
        at async bar (file:///Users/user/async-stack-trace/bar.js:4:5)
        at async file:///Users/user/async-stack-trace/foo.js:4:1 {
      errno: -2,
      code: 'ENOENT',
      syscall: 'open',
      path: 'some'
    }
    
    Node.js v22.1.0
    
    

    ES modules with async function (no top level await).

    foo.js

    import { bar } from './bar.js'
    
    async function main() {
      await bar()
    }
    
    main()
    

    Which after running node foo.js results in a similar stack trace:

    node:internal/fs/promises:638
      return new FileHandle(await PromisePrototypeThen(
                            ^
    
    Error: ENOENT: no such file or directory, open 'some'
        at async open (node:internal/fs/promises:638:25)
        at async readFile (node:internal/fs/promises:1251:14)
        at async bar (file:///Users/user/async-stack-trace/bar.js:4:5)
        at async main (file:///Users/user/async-stack-trace/foo.js:4:3) {
      errno: -2,
      code: 'ENOENT',
      syscall: 'open',
      path: 'some'
    }
    
    Node.js v22.1.0
    

    CommonJS

    package.json

    "type": "commonjs"
    

    bar.js

    const { readFile } = require('node:fs/promises')
    
    exports.bar = async function bar() {
      await readFile('some')
    }
    

    foo.js

    const { bar } = require('./bar.js')
    
    async function main() {
      await bar()
    }
    
    main()
    

    After running node foo.js:

    node:internal/fs/promises:638
      return new FileHandle(await PromisePrototypeThen(
                            ^
    
    Error: ENOENT: no such file or directory, open 'some'
        at async open (node:internal/fs/promises:638:25)
        at async readFile (node:internal/fs/promises:1251:14)
        at async bar (/Users/user/async-stack-trace/src/bar.js:4:3)
        at async main (/Users/user/async-stack-trace/src/foo.js:4:3) {
      errno: -2,
      code: 'ENOENT',
      syscall: 'open',
      path: 'some'
    }
    
    Node.js v22.1.0