Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected ENOENT when using createReadStream #338

Open
lostfictions opened this issue Sep 23, 2021 · 4 comments
Open

Unexpected ENOENT when using createReadStream #338

lostfictions opened this issue Sep 23, 2021 · 4 comments

Comments

@lostfictions
Copy link

Hi there, I'm trying mock-fs out. I might be misunderstanding, but it sounds like it creates an in-memory filesystem that can be written to as well as read from, right? Unfortunately, I seem to be getting an error when I write a file to the os tmpdir and then try to read it back via createReadStream. This works fine on the real filesystem.

Here's a minimal repro, using mock-fs 5.1.0 and jest 27.2.1.

const mockFs = require("mock-fs");
const { createReadStream } = require("fs");
const { writeFile, unlink } = require("fs/promises");
const { join } = require("path");
const { tmpdir } = require("os");

beforeEach(() => {
  mockFs();
});

afterEach(() => {
  mockFs.restore();
});

describe("repro", () => {
  it("gives an ENOENT when it shouldn't", async () => {
    const path = join(tmpdir(), `tmpfile-${Math.random()}.png`);
    await writeFile(path, Buffer.from("whatever"));

    const rs = createReadStream(path);
    rs.close();

    await unlink(path);

    expect(true);
  });
});

Here's the output I get:

$ repro/node_modules/.bin/jest
 FAIL  ./repro.test.js
  repro
    ✕ gives an ENOENT when it shouldn't (8 ms)

  ● repro › gives an ENOENT when it shouldn't

    Unhandled error. (Error {
      message: "ENOENT, no such file or directory '/tmp/tmpfile-0.567831857611433.png'",
      code: 'ENOENT',
      errno: -2,
      path: '/tmp/tmpfile-0.567831857611433.png'
    })

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.281 s, estimated 1 s
Ran all test suites.

Commenting out const rs = createReadStream(path); rs.close(); causes the test to pass.

@3cp
Copy link
Collaborator

3cp commented Sep 24, 2021

I can reproduce the error in Nodejs v16, but not v14. Don't need to use jest to reproduce the bug.
@Rugvip it looks there is edge case for fs.createReadStream, it tries to access the real file system.

const mockFs = require("mock-fs");
const fs = require("fs");
const { writeFile, unlink } = require("fs/promises");
const { join } = require("path");
const { tmpdir } = require("os");

mockFs();
// mockFs.restore();

async function test() {
  const path = join(tmpdir(), `tmpfile-${Math.random()}.png`);
  await writeFile(path, Buffer.from("whatever"));

  const rs = fs.createReadStream(path);
  // rs.close();
  await unlink(path);
}

test().then(() => console.log("DONE"));
Error: ENOENT, no such file or directory '/var/folders/v_/y1wwsg8d7fvcr981nq9l89x00000gn/T/tmpfile-0.870533120229688.png'

@3cp
Copy link
Collaborator

3cp commented Nov 6, 2021

Update: I found there is a very strange timing issue. My log shows the logic inside fs.createReadStream seems being delayed after the await unlink(path), that's why it cannot find the file (it's already unlinked).

This can be approved with 2 ways:

  1. remove await unlink(path);.
  2. or add a delay before unlink.
async function delay() {
  return new Promise(resolve => {
    setTimeout(resolve, 500);
  });
}

// ...
await delay();
await unlink(path);

Since nodejs v14 has no such issue, there might be some bug or edge case in nodejs v16.

@3cp
Copy link
Collaborator

3cp commented Nov 6, 2021

I can reproduce the same issue WITHOUT using mock-fs in nodejs v16.13.0, so this is probably an unexpected bug in nodejs v16+ itself.

const fs = require("fs");
const { writeFile, unlink } = require("fs/promises");
async function test() {
  const path = `tmpfile-${Math.random()}.png`;
  await writeFile(path, Buffer.from("whatever"));
  const rs = fs.createReadStream(path);
  // it doesn't matter whether we call close()
  rs.close();
  await unlink(path);
}

test().then(() => console.log("DONE"));
> node test.js
DONE
node:events:368
      throw er; // Unhandled 'error' event
      ^

Error: ENOENT: no such file or directory, open 'tmpfile-0.16589223939625652.png'
Emitted 'error' event on ReadStream instance at:
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: 'tmpfile-0.16589223939625652.png'
}

Note the error is printed after "DONE" (means test() is resolved successfully...)

@KenEucker
Copy link

This also appears to be an issue impacting readFileSync. I tried to create a stream from a file loaded by readFileSync but I get the same ENOENT error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants