Why doesn't node.js garbage collect system resources (such as file handles)?

I've been doing a bunch of async file I/O work on node.js and it strikes me that one has to be extremely careful to catch all error paths in order to not accidentally leak file descriptors in error conditions. But, Javascript is a garbage collected language and this is exactly the kind of things that you're not supposed to have to worry about in a garbage collected environment.

If the variable holding the file descriptor goes out of scope or becomes unreachable from your code it seems that the node.js garbage collector should know that and properly clean up the system resource for you. Javascript in a browser does this with DOM objects. If a DOM object has been removed from the DOM and is held in a Javascript variable that then goes out of scope or becomes unreachable, the browser will automatically clean it up for you. You don't have to manually delete it. So, certainly this level of integration with non-JS resources is possible.

So, I'm wondering why node.js doesn't have this capability as it seems it would be very useful and would make it a much more robust server development environment?

Is this just a case of node.js being fairly early in its development and this might be a logical improvement over time, but for now there are more important things to work on?

Is this a case of node.js staying at arm's length with the V8 engine and thus not getting integrated to the level that would be required to garbage collect system handles?

Is there some conceptual difficulty that makes doing GC of file handles or other system resources like them impractical?

As jfriend00 said, the fact that file descriptors are just numbers in node makes it a problem. The file descriptor may have been copied, and there's no way to track this.

Further, file descriptors from the master process can be shared with children (see the cluster module), which means that even if a process could determine that there are no more references to a particular file descriptor, a different process may have a copy of it.

Its possible that the requirement that file descriptors be shareable in this way is what drove the file descriptor as number decision, since it wouldn't be possible to pass an object with invisible internals between processes.