Handling errors with gulp watch and gulp-plumber

Not as straightforward as it seems.

tldr;

If you use gulp-plumber in combination with gulp.watch, make sure you call this.emit('end') in gulp-plumber's handleError callback:

gulp.src('./less/**/*.less')  
    .pipe(plumber({
        errorHandler: function (err) {
            console.log(err);
            this.emit('end');
        }
    }))
    .pipe(less)
    .dest(gulp.dest('./build'));

Note: Thanks @j_rubenz for the heads up on a typo!

Gulp watch: rebuilding assets when files change

If you're working on a front-end project that compiles assets with gulp, you should probably be using gulp's watch util. As you might expect gulp.watch watches a glob of files and runs a task (or an array of tasks) when it sees changes are made.

Here's a simple example, assuming build is a task defined somwhere in your gulpfile:

gulp.task('watch', ['build'], function () {  
    gulp.watch(['./js/**/*.js', './css/**/*.less'], 'build');
});

In this case, when we run gulp watch we get an initial build, when we start the task, and then a subsequent build every time watch picks up a change.

What about errors?

If you save syntax errors on watched files and gulp is unable to compile your code, your watch task will probably crash.

So how do we handle these errors gracefully? Like any other node streams, you can listen on the error event in your gulp tasks:

// This kinda works,
// but check out the section below on gulp-plumber
gulp.src('./less/**/*.less')  
    .pipe(less)
    .on('error', function (err) {
      console.log(err);
    })
    .dest(gulp.dest('./build'));

However, due to the nature of gulp error handling, it's generally a good idea to use gulp-plumber instead.

Use gulp-plumber!

Inside your individual tasks, e.g. compiling less, all you have to do pipe your readable stream through gulp-plumber.

If you're using watch, you also need to make sure you call this.emit('end') in the handleError callback to make sure gulp knows when to end the task that errored out, and it will continue to work once you fix the problem and watch calls it again.

If you don't emit an end event, your error will not cause a crash, but your task will just hang forever and you'll have to re-start the watch process anyway.

npm install --save-dev gulp-plumber  
var plumber = require('gulp-plumber');

...

gulp.src('./less/**/*.less')  
    .pipe(plumber({
        handleError: function (err) {
            console.log(err);
            this.emit('end');
        }
    }))
    .pipe(less)
    .dest(gulp.dest('./build'));