Skip to content

fs.openAsBlob reports a missing file as ERR_INVALID_ARG_VALUE, discarding the ENOENT #65514

Description

@gildas-lormeau

Version

v26.7.0 (reproduced; code path unchanged on main)

Platform

macOS (Darwin 25.5.0, arm64) — not platform-specific

Subsystem

fs

What steps will reproduce the bug?

const { openAsBlob } = require("node:fs");
try {
  openAsBlob("./does-not-exist.txt");
} catch (error) {
  console.log(error.name, error.code, error.message);
  // TypeError ERR_INVALID_ARG_VALUE Unable to open file as blob
}

Always reproduces. Same result for a permission failure — every open/stat failure produces the identical error.

What is the expected behavior? Why is that the expected behavior?

A rejection (or at least an error) equivalent to what every other fs open/stat API produces for a missing file: a system error with code: "ENOENT", plus syscall and path. ERR_INVALID_ARG_VALUE (a TypeError) says the argument is malformed, but the path here is a perfectly valid argument — the file just doesn't exist. The docs for openAsBlob don't document any different error contract.

What do you see instead?

TypeError [ERR_INVALID_ARG_VALUE]: Unable to open file as blob — thrown synchronously (the synchronous-throw half is already tracked in #62418), with no errno, syscall, or path. ENOENT, EACCES, and any other underlying failure are indistinguishable.

Additional information

Mechanism:

  1. lib/fs.js openAsBlob() calls createBlobFromFilePath(path) synchronously and wraps the result in PromiseResolve.
  2. src/node_blob.cc BlobFromFilePath() calls DataQueue::CreateFdEntry(env, args[0]) and, when it gets nullptr, throws THROW_ERR_INVALID_ARG_VALUE(env, "Unable to open file as blob").
  3. src/dataqueue/queue.cc FdEntry::Create() returns nullptr for any negative uv_fs_stat/open result, discarding the libuv status code — this is where the ENOENT is lost.

Note that the pending fix for #62418 (PR #62655) only converts the synchronous throw into a promise rejection; its new test asserts code: 'ERR_INVALID_ARG_VALUE' for openAsBlob('does-not-exist'), which would enshrine the misleading code. It would be good to fix the error identity at the same time: have the native side (or FdEntry::Create) propagate the uv status and throw a proper UVException (ENOENT/EACCES/… with syscall/path), as fs.open does.

Real-world impact: the generic ERR_INVALID_ARG_VALUE sent us debugging "path validation" changes for a test-suite failure whose actual cause was simply a cwd-relative path to a file that didn't exist — an ENOENT with the path attached would have made it a ten-second diagnosis.

Related observation while testing (can file separately if preferred): openAsBlob("./some-directory") succeeds and returns a Blob sized to the directory's stat size; reading it later fails with NotReadableError: The blob could not be read. An EISDIR at open time would be more helpful there too.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions