The biggest problem with forwarding errors happens when you have
duplex streams that forward to one another, and as Martin points out,
streams that emit errors in different circumstances.
For example, let's say that you added something like this in the
pipe() function:
src.on('error', function(er) {
dest.emit('error', er);
});
What happens when you do this?
// encryptor service
net.createServer(function (socket) {
socket.pipe(new Encryptor()).pipe(socket);
});
If the socket emits error, it forwards to the encryptor, which
forwards it to the socket, and it's an infinite loop. Now every
encryption error is also a RangeError!
You *could* make this work by keeping track of whether a stream's
error events are already forwarded, but that's just more state
tracking and complexity, and it ends up being somewhat
indeterministic. For example, these two servers would be
error-forwarded differently, though they should be semantically
equivalent:
net.createServer(function (socket) {
// all errors emit on socket
var e = new Encryptor();
e.pipe(socket).pipe(e);
});
net.createServer(function (socket) {
// all errors emit on encryptor
var e = new Encryptor();
socket.pipe(e).pipe(socket);
});
To Martin's point, this also obscures the source of the error. Though
it's more verbose, stuff like this is actually quite important:
fs.createReadStream(tarball)
.on('error', handleFileReadError)
.pipe(gzip.Gunzip())
.on('error', handleUnzipError)
.pipe(tar.Extract({ path: targetPath }))
.on('error', handleTarExtractError)
.on('close', cb)
All those different errors have different information, metadata, etc.,
and in many cases, if you don't know the object that emitted them,
then you don't have a lot of insight into what error message you
should print out. ("Your program says 'illegal data', what's going
wrong?" Good luck debugging that!)
The moral of the story is that you must either attach error listeners
to all streams you touch, or accept that they will throw (and perhaps
gather all throws into a domain). For example, if you really *wanted*
to only have a single error handler you could do this:
var d = domain.create();
d.on('error', handleAllErrors);
d.run(function() {
fs.createReadStream(tarball)
.pipe(gzip.Gunzip())
.pipe(tar.Extract({ path: targetPath }))
.on('close', cb);
});
This is actually not so bad. Domains add a bit of metadata to the
error object, so it's not too hard to figure out whether it was an
"organic" throw, or an error event that was emitted by some object.