Repository navigation
fs.openAsBlob() does not work properly for files > 2GB #52585
Copy link
Copy link
Open
Labels
fsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
Description
Version
v21.7.3 (and v20.12.2)
Platform
Darwin bender.local 23.4.0 Darwin Kernel Version 23.4.0: Fri Mar 15 00:10:42 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6000 arm64
Subsystem
fs
What steps will reproduce the bug?
Steps to reproduce bug:
- Generate a 3 GB file with
dd if=/dev/random of=random.bin bs=1048576 count=3072 - Launch node (v21.7.3 and v20.12.2 both repro for me)
const b = await fs.openAsBlob("random.bin")b.slice(2**31-2, 2**31-1)<-- worksb.slice(2**31-1, 2**31)<-- returns a slice of zero lengthb.slice(2**31, 2**31+1)<-- crashes
Full textual output of doing this given below.
Also, for files greater than 4GB, the size of the blob is wrong. It appears to be exactly 4*(2**30) less than the actual size. For example, for a 5*(2**30) byte file, the blob size is reported as 1073741824, which is (2**30). And, for a file that is exactly 4*(2**30), it reports a size of zero.
How often does it reproduce? Is there a required condition?
Always reproduces.
What is the expected behavior? Why is that the expected behavior?
Can operate on the Blob normally.
What do you see instead?
Described above. See output below.
Additional information
$ dd if=/dev/random of=random.bin bs=1048576 count=3072
3072+0 records in
3072+0 records out
3221225472 bytes transferred in 4.213743 secs (764457033 bytes/sec)
$ ls -l random.bin
-rw-r--r-- 1 jbrandt staff 3221225472 Apr 18 15:04 random.bin
$ node -v
v21.7.3
$ node
Welcome to Node.js v21.7.3.
Type ".help" for more information.
> const b = await fs.openAsBlob("random.bin")
undefined
> b
Blob { size: 3221225472, type: '' }
> b.slice(2**31-2, 2**31-1)
Blob { size: 1, type: '' }
> b.slice(2**31-1, 2**31)
Blob { size: 0, type: '' }
> b.slice(2**31, 2**31+1)
# node[96070]: static void node::Blob::ToSlice(const FunctionCallbackInfo<v8::Value> &) at ../src/node_blob.cc:247
# Assertion failed: args[0]->IsUint32()
----- Native stack trace -----
1: 0x1041dfc0c node::Assert(node::AssertionInfo const&) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
2: 0x10589b4a4 node::Blob::ToSlice(v8::FunctionCallbackInfo<v8::Value> const&) (.cold.2) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
3: 0x1041a8994 node::Blob::ToSlice(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
4: 0x104c32a38 Builtins_CallApiCallbackGeneric [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
5: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
6: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
7: 0x104c2e8ac Builtins_JSEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
8: 0x104c2e594 Builtins_JSEntry [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
9: 0x1044daef8 v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
10: 0x1044db0e0 v8::internal::Execution::CallScript(v8::internal::Isolate*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
11: 0x10439ef68 v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::Data>) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
12: 0x1041c8d1c node::contextify::ContextifyScript::EvalMachine(v8::Local<v8::Context>, node::Environment*, long long, bool, bool, bool, v8::MicrotaskQueue*, v8::FunctionCallbackInfo<v8::Value> const&) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
13: 0x1041c87fc node::contextify::ContextifyScript::RunInContext(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
14: 0x104c32a38 Builtins_CallApiCallbackGeneric [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
15: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
16: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
17: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
18: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
19: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
20: 0x10a6531d0
21: 0x10a6ad6ac
22: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
23: 0x104c30b84 Builtins_InterpreterEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
24: 0x10a66b508
25: 0x10a6767a8
26: 0x10a66cb7c
27: 0x10a65307c
28: 0x10a6ad6ac
29: 0x10a6a73f0
30: 0x104c69e04 Builtins_GeneratorPrototypeNext [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
31: 0x10a65c654
32: 0x10a65307c
33: 0x10a6ad6ac
34: 0x10a66d390
35: 0x10a6754b8
36: 0x10a66e24c
37: 0x10a675b48
38: 0x10a64f78c
39: 0x104c2e8ac Builtins_JSEntryTrampoline [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
40: 0x104c2e594 Builtins_JSEntry [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
41: 0x1044daef8 v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
42: 0x1044da478 v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
43: 0x1043b2404 v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
44: 0x104100fc0 node::InternalMakeCallback(node::Environment*, v8::Local<v8::Object>, v8::Local<v8::Object>, v8::Local<v8::Function>, int, v8::Local<v8::Value>*, node::async_context) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
45: 0x104117c00 node::AsyncWrap::MakeCallback(v8::Local<v8::Function>, int, v8::Local<v8::Value>*) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
46: 0x1042af900 node::StreamBase::CallJSOnreadMethod(long, v8::Local<v8::ArrayBuffer>, unsigned long, node::StreamBase::StreamBaseJSChecks) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
47: 0x1042b0f7c node::EmitToJSStreamListener::OnStreamRead(long, uv_buf_t const&) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
48: 0x1042b526c node::LibuvStreamWrap::OnUvRead(long, uv_buf_t const*) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
49: 0x1042b59a0 node::LibuvStreamWrap::ReadStart()::$_1::__invoke(uv_stream_s*, long, uv_buf_t const*) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
50: 0x104c1a390 uv__stream_io [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
51: 0x104c21da4 uv__io_poll [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
52: 0x104c0fda4 uv_run [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
53: 0x1041016f0 node::SpinEventLoopInternal(node::Environment*) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
54: 0x10421f754 node::NodeMainInstance::Run(node::ExitCode*, node::Environment*) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
55: 0x10421f4d4 node::NodeMainInstance::Run() [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
56: 0x10419eb48 node::Start(int, char**) [/Users/jbrandt/.nvm/versions/node/v21.7.3/bin/node]
57: 0x19e7060e0 start [/usr/lib/dyld]
----- JavaScript stack trace -----
1: slice (node:internal/blob:265:21)
2: REPL5:1:3
3: runInThisContext (node:vm:136:12)
4: defaultEval (node:repl:598:22)
5: bound (node:domain:432:15)
6: runBound (node:domain:443:12)
7: onLine (node:repl:927:10)
8: emit (node:events:531:35)
9: emit (node:domain:488:12)
10: [_onLine] (node:internal/readline/interface:416:12)
zsh: abort nodeReactions are currently unavailable
Metadata
Metadata
Assignees
Labels
fsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
Activity
[-]`fs.openAsBlob` does not work properly for files > 2GB[/-][+]`fs.openAsBlob()` does not work properly for files > 2GB[/+]joelrbrandt commentedon Apr 18, 2024
FWIW, this also reproduces on a
linux/arm64container image running in Docker for Mac.kylo5aby commentedon Apr 19, 2024
Hi @joelrbrandt , In the implementation of
Blob.slice, it's hinted that thestartandendshould be within theuint32range,Since it is never documented and actually there allows
Blobhave index out of rangeuint32, should it be documented to make it clear or validate the range of index instead of using such asstart | 0andend | 0? /cc @nodejs/bufferjoelrbrandt commentedon Apr 23, 2024
@kylo5aby thanks for your quick response (and sorry for my slow follow-up).
I'm a bit confused by this part of your response. Where is this hinted? In the node implementation?
I don't see any mention of a 32-bit limitation in the File API spec. So, it doesn't seem like the
Blob.prototype.slice()API should be limited to the uint32 range. Also, if it were limited to the uint32 range, it should go up to2**32, not2*31. But, the.slice()API supports negative values (distance from end), so maybe you meant int32?Independent of the issue with
.slice(), node reports the wrong size for Blobs that are larger than 4GB.Interestingly, if I get a stream of the Blob (with
Blob.prototype.stream()), I can read all the bytes in Blobs larger than 4GB.See below where I do the following:
fs.openAsBlobFinally, here's a codepen that can be used to check that files > 4GB work fine in all major modern browsers (open console to see output): https://codepen.io/joelrbrandt/pen/wvZRXvY
joelrbrandt commentedon Apr 23, 2024
Finally, if
fs.openAsBlob()is going to have a 4GB limit (andBlob.prototype.slice()is going to have a 2GB limit), it would certainly be more ideal if they threwRangeErrorexceptions rather than crashing node on out-of-range values.Buffer.alloc()does this:kylo5aby commentedon Apr 23, 2024
Hi @joelrbrandt, Thank you for the information.
fs.openAsBlob(), if it read a file with size larger than 4GB, the generated blob has a wrongsizeas you mentioned:There maybe a bug in
fs.openAsBlob(), it wrongly set thesize. do you think it works as expected? @jasnell . AFAIK,new Blob()can correctly set thesize.2. the uint32 range is for
Blob.sliceinstead ofBlob, it allows negative index as you mentioned, and will converted to positive under the hood and check whether it under the uin32 range. I have a PR #52588 to try to solve if there is a largestartorend(larger than 2^31)joelrbrandt commentedon Apr 23, 2024
@kylo5aby
I'm not sure if this is accurate, either. Trying to construct a new Blob from a set of ArrayBuffers that total a size greater than 4GB also fails:
However, this code also works fine in a browser, resulting in a 5GB
Blob.kylo5aby commentedon Apr 24, 2024
You are right. In versions up to v21, on 64-bit systems, the maximum size of a Buffer was 4GB. However, in the version I last used, compiled from the main branch, this limit has been exceeded, that's why I said
new Blobis ok to create a blob larger than 4GB.Conversely,
fs.openAsBlobcan successfully create blobs larger than 4GB, but there might be issues with the size.buffer: remove lines setting indexes to integer value
buffer: remove lines setting indexes to integer value
buffer: remove lines setting indexes to integer value
buffer: remove lines setting indexes to integer value
heguro commentedon Dec 10, 2024
It appears to persist this issue for files >4GB in latest Node.js v23.3.0 and v20.18.1:
Also:
Tested on
linux/amd64anddarwin/arm64.1.2.5
yarlc commentedon Mar 5, 2026
Why hasn't this issue been resolved for several years? Is it really that difficult? Both deno and bunjs are correct!
tcyrus commentedon Apr 25, 2026
Bun handles this by using a custom
u52type (Number.MAX_SAFE_INTEGER). Deno implementsBlobs in a WebIDL extension (deno_webidlconverts betweenunsigned long longandNumber).The
Blob.sizeissue is because of the usage ofV8::Uint32inBlobFromFilePathnode/src/node_blob.cc
Lines 123 to 126 in 9c4ca0a
Blob::ToSliceis also restricted toV8::Uint32node/src/node_blob.cc
Lines 272 to 283 in 9c4ca0a
The existing
CheckNumberToSizeshould be able to convert aV8::Numberto asize_t:node/src/node_buffer.cc
Lines 1640 to 1654 in 6a3d80f
This is my best guess on how to convert a
size_tto aV8::Number:github-actions commentedon Jul 25, 2026
This issue has been marked as stale due to 90 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.
heguro commentedon Jul 25, 2026
Still persist in Node.js v26.5.0. Same result as #52585 (comment)
docs(node): note openAsBlob bugs with files over 2 GiB (#1879)