Gulp: Watch stops on errors (Figure out better error handling)

Created on 10 Feb 2014  ·  58Comments  ·  Source: gulpjs/gulp

This is spread around 10 issues on all sorts of projects. I'm consolidating them all here.

enhancement

Most helpful comment

Watch waits for end, so emitting end in error handler solves most of the cases.

function handleError(err) {
  console.log(err.toString());
  this.emit('end');
}

gulp.task('onerror', function () {
  return gulp.src('somefile')
    .pipe(someStream())
    .on('error', handleError);
});

gulp.task('plumber', function () {
  return gulp.src('somefile')
    .pipe(pulmber({ errorHandler: handleError }))
    .pipe(someStream());
});

gulp.task('multipipe', function () {
  return multipipe(
    gulp.src('somefile'),
    someStream()
  ).on('error', handleError);
});

(We don't need errorHandler for plumber if all plugins are written like the example in the guideline. In that case, plumber suppresses error and end is emitted somehow. However, for callback(err) of through2, plumber suppresses error but end is not emitted.)

While those tasks work well with watch, they always exit with 0, which is problematic in Continuous Integration and such.

All 58 comments

Watch waits for end, so emitting end in error handler solves most of the cases.

function handleError(err) {
  console.log(err.toString());
  this.emit('end');
}

gulp.task('onerror', function () {
  return gulp.src('somefile')
    .pipe(someStream())
    .on('error', handleError);
});

gulp.task('plumber', function () {
  return gulp.src('somefile')
    .pipe(pulmber({ errorHandler: handleError }))
    .pipe(someStream());
});

gulp.task('multipipe', function () {
  return multipipe(
    gulp.src('somefile'),
    someStream()
  ).on('error', handleError);
});

(We don't need errorHandler for plumber if all plugins are written like the example in the guideline. In that case, plumber suppresses error and end is emitted somehow. However, for callback(err) of through2, plumber suppresses error but end is not emitted.)

While those tasks work well with watch, they always exit with 0, which is problematic in Continuous Integration and such.

Important issue IMHO, as this really slows my development time currently.

I think we should be able to mark task as failed but still run it.

  • gulp watch task could ignore this error (only log it in console) and keep running.
  • default behaviour would be to console.log(err); process.exit(1) - so task initted in CLI would simply fail.

But this would make it dependant on robrich/orchestrator#20

Yes, I'm getting a ton of issues about this...

While the issue feels a lot more general than watch I'm glad this is on the radar. I just finished my first gulp flow (v0.1, anyway) and I'm firing up express instances and watching. A single typo in a scss file kills express instances and lr just as much as watch…

I know, not constructive.

My pitch was a flag one could pass into .run() (or whatever we rename it to) that doesn't stop on errors at all, just pass the error to the callback. Watch could then call it with the flag set, and everything else could call it with the flag unset. I see how robrich/orchestrator#20 is quite similar though -- good call.

Is there any progress, it takes too much time.

@phated @robrich Progress update on the next orchestrator?

Check out orchestrator's develop branch and github.com/robrich/gulp develop branch. Give it a whirl and see if it's sufficient.

Just a quick update: I was waiting until the new task system in gulp 4.0 for this to be fixed but it seems to be a large enough issue that I put a hotfix in place in 3.6. Watch will no longer stop and the process will no longer exit just because an error was thrown.

Hi, I'm relatively new to gulp, but I've updated to the latest version (3.6) and gulp-ruby-sass (0.2.0) is still stopping the watch task when a syntax error occurs:

[gulp] Error in plugin 'gulp-ruby-sass': 
Syntax error: Invalid CSS after " */": expected "}", was ""
        on line 51 of /Users/matt/Git/client/_dev/scss/style.scss

    at ChildProcess.<anonymous> (/Users/matt/Git/client/node_modules/gulp-ruby-sass/index.js:73:25)
    at ChildProcess.EventEmitter.emit (events.js:98:17)
    at maybeClose (child_process.js:743:16)
    at Process.ChildProcess._handle.onexit (child_process.js:810:5)

What other details do I need to provide to be of use?

@mattkersley your CLI and local version are up to date with 3.6?

@contra your update in 3.6.0 (both local and cli version) didn't help me. I use jshint to validate my files:

gulp.task('js', function() {
    return gulp.src('src/**/*.js')
        .pipe(jshint('.jshintrc'))
        .pipe(jshint.reporter('jshint-stylish'))
        .pipe(jshint.reporter('fail'))
        .pipe(concat('public/scripts.js'));
});
gulp.task('watch', function () {
    gulp.watch('src/**/*.js', ['js']);
});

Gulp exits from watch every time, when it has found a syntax error with the following stacktrace:

throw err
              ^
Error: JSHint failed for app/app.js
    at /home/just-boris/coding/tms/node_modules/gulp-jshint/src/reporters.js:8:15
    at wrappedMapper (/home/just-boris/coding/tms/node_modules/gulp-jshint/node_modules/map-stream/index.js:76:19)
    at Stream.stream.write (/home/just-boris/coding/tms/node_modules/gulp-jshint/node_modules/map-stream/index.js:88:21)
    at Stream.ondata (stream.js:51:26)
    at Stream.EventEmitter.emit (events.js:104:17)

Is there a way to run watch continuously and to can start again after the error?

Remove the line:

jshint.reporter("fail")

On Apr 2, 2014, at 11:30 AM, Boris Serdyuk [email protected] wrote:

@contra your update in 3.6.0 (both local and cli version) didn't help me. I use jshint to validate my files:

gulp.task('js', function() {
return gulp.src('src//.js')
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('jshint-stylish'))
.pipe(jshint.reporter('fail'))
.pipe(concat('public/scripts.js'));
});
gulp.task('watch', function () {
gulp.watch('src/
/.js', ['js']);
});
Gulp exits from watch every time, when it has found a syntax error with the following stacktrace:

throw err
^
Error: JSHint failed for app/app.js
at /home/just-boris/coding/tms/node_modules/gulp-jshint/src/reporters.js:8:15
at wrappedMapper (/home/just-boris/coding/tms/node_modules/gulp-jshint/node_modules/map-stream/index.js:76:19)
at Stream.stream.write (/home/just-boris/coding/tms/node_modules/gulp-jshint/node_modules/map-stream/index.js:88:21)
at Stream.ondata (stream.js:51:26)
at Stream.EventEmitter.emit (events.js:104:17)
Is there a way to run watch continuously and to can start again after the error?


Reply to this email directly or view it on GitHub.

hmmm... possibly not... how do I go about updating my cli version? (sorry for the newbie question)

I've run npm install gulp -g, but when I run npm gulp -v I'm not getting 3.6.0.

never mind... just did the following:

gulp -v
[gulp] CLI version 3.6.0
[gulp] Local version 3.6.0

So both up to date, still breaks when syntax errors occur :(

Edit:
Here is my gulpfile for reference - https://gist.github.com/mattkersley/9939822

@jasonrhodes yes, the error will be removed. But it means that I wouldn't know about an error, for example, in travis. So, this reporter is required here, and my issue like the @mattkersley case.

@mattkersley Not sure what's going on regarding your situation and the potential fix in 3.6 but as a temporary work around I set up my lint task like this (i.e. manually check for the fail and only beep if it occurs, this way it should operate quite happily when called from a watch): https://github.com/spenceralger/gulp-jshint/pull/30#issuecomment-37769838

@contra I'm updated to the latest version 3.6.0, but watch is still exiting on browserify errors.

@contra hey man, did you have a chance to check out my gulp config? any ideas why the fix isn't working for me?

Cheers.

For everyone who is having issues:

Can you try adding something like this to your gulpfile?

gulp.on('err', function(err){
  console.log(err);
});

Adding this into my gulpfile didn't resolve my issue. Using gulp 3.6, an error during the task is still removing it from watch's tasks.

The same.

FYI, the approach outlined in these SO answers worked for me.

The only caveat being that you need to add the .on('error', handleError) in the right place. E.g. if you're trying to catch errors coming from browserify:

Works:

  gulp.src('main.js')
    .pipe(browserify().on('error', handleError))
    .pipe(gulp.dest('build'));

Works:

  gulp.src('main.js')
    .pipe(browserify())
    .on('error', handleError)
    .pipe(gulp.dest('build'));

Doesn't work:

  gulp.src('main.js')
    .pipe(browserify())
    .pipe(gulp.dest('build'))
    .on('error', handleError);

@mrienstra's workaround he found on StackOverflow worked for me for the time being. My issues were with gulp-compass 1.1.8, and gulp 3.6.1 (both local and cli)

For reference, it was the this.emit('end'); that is now needed:

.pipe(plugins.rubySass({
    style: sassStyle, sourcemap: sourceMap, precision: 2
}))
.on('error', function(err){
    new gutil.PluginError('CSS', err, {showStack: true});
        this.emit('end');
})

Hi!
I'm using gulp 3.8.7 and I had the same issue with gulp-compass with gulp-plumber. I tried adding the same line this.emit('end') and it works perfectly.

  this.emit('end');. Thanks.

:+1: this.emit('end') saves the day. Thanks @mikestreety.

Any ideas if that terminates the stream entirely or does it just continue without any files?

i.e:

gulp.task('less', function () {
    return gulp.src('./css/main.less')
        .pipe($.less())
        .on('error', function (err) {
            $.util.log(err.message);
            this.emit('end');
        })
        .pipe($.autoprefixer('last 2 versions'))
        .pipe($.minifyCss())
        .pipe($.rename('main.css'))
        .pipe(gulp.dest('./css/'))
        .pipe($.size({
            showFiles: true
        }));
});

What happens to rest of stream on error?

:+1:

.pipe(gulpLess(options))
.on('error', function (err) {
  var displayErr = gutil.colors.red(err);
  gutil.log(displayErr);
  gutil.beep();
  if (options.dontFail) {
    this.emit('end');
  } else {
    throw displayErr;
  }
})

worked great for me. It also allows me to make sure that my build fails when I want it to but my watch doesn't stop.

This is another (bit hacky) solution which allows you to stop the the build execution and return consistently to the watch state:

  • create a task which continuously re-spawn gulp watch
  • put 'process.exit(1)' in the error handler after printing the error message, instead of crashing/hanging, gulp will just restart
  • bonus: you can watch gulpfile.js and respawn the watch on change.

The downside is that it takes longer on error than on green build, but it is faster than reading the error message anyway.

See example here: https://github.com/appium/DynamicApp/blob/master/injector/gulpfile.js

@sebv That's really helpful, very smart implementation! Thank you for it and for the bonus! 👍

@maxgrass I've wrapped it here: https://github.com/appium/appium-gulp-plugins

I faced the same problem.

omitting return in the task solves my problem.

//code works
gulp.task('asset:uglify',function(){
    gulp.src('js/*js')
        .pipe(uglify())
        .on('error',console.log.bind(console)) //Error handling and 
        .pipe(gulp.dest('public/'));
});

// code that doesn't works

gulp.task('asset:uglify',function(){
    return gulp.src('js/*js')
        .pipe(uglify())
        .on('error',console.log.bind(console)) //Error handling and 
        .pipe(gulp.dest('public/'));
});


@lokeshjainRL the problem you'll have with the code that works is that that doesn't pass along the pipe, and you'll not be able to tell when the task is finished, so you'll not be able to depend on that task because tasks that depend on that task will start as soon as this task is started. And the fact that you're not passing along the pipe is probably the reason why it doesn't break the watch-task on error.

@Karl-Gustav :+1: that is a great info. thanks.

Just to add to this I do still get errors and it blocks and finishes.

***HandleErrors Feil**
var notify = require("gulp-notify");

module.exports = function() {

  var args = Array.prototype.slice.call(arguments);

  // Send error to notification center with gulp-notify
  notify.onError({
    title: "Compile Error",
    message: "<%= error.message %>"
  }).apply(this, args);

  // Keep gulp from hanging on this task
  this.emit('end');
};

***gulp task**
gulp.task('sass', function() {
  return gulp.src(amazon)
    .pipe(changed(DEST, { extension: '.css' }))
    .pipe(plumber({
        errorHandler: handleErrors
    }))
    .pipe(sourcemaps.init())
    .pipe(sass({}))
    .pipe(sourcemaps.write('./'))
    .on('error', handleErrors)
    .pipe(gulp.dest(DEST))
});
C:\var\www\mobile 2>gulp sass
[12:51:35] Using gulpfile C:\var\www\mobile 2\gulpfile.js
[12:51:35] Starting 'sass'...
[12:51:35] Finished 'sass' after 13 ms
[12:51:35] gulp-notify: [Compile Error] stylesheets\Articles.scss
  321:115  error reading values after :
[12:51:35] gulp-notify: [Compile Error] stylesheets\sectionBasic.scss
  436:29  error reading values after 200px

I've tried all fixes and also removed everything but the sass() with errors and dest but no luck.

I've been using this as a general solution. The advantage is that non-watch tasks which don't depend on this keep behaving normally.

// Watch tasks should depend on suppress-errors - it will force all stream pipes to print but not crash on error
gulp.task('suppress-errors', function(){
    function monkeyPatchPipe(o){
        while(!o.hasOwnProperty('pipe')){
            o = Object.getPrototypeOf(o);
            if(!o){
                return;
            }
        }
        var originalPipe = o.pipe;
        var newPipe = function(){
            var result = originalPipe.apply(this, arguments);

            if(!result.pipe['monkey patched for suppress-errors']){
                monkeyPatchPipe(result);
            }

            return result.on('error', function (err) {
                gutil.log(gutil.colors.yellow(err));
                gutil.beep();
                this.emit('end');
            });
        };
        newPipe['monkey patched for suppress-errors'] = true;
        o.pipe = newPipe;
    }
    monkeyPatchPipe(gulp.src(""));
});

@jamiehutber you ever find a solution to your problem? I'm running into the same thing - using plumber with an error handler i have defined/exported from a different file. ive played around with bind to see if it was a context issue, but couldn't get anything working

@robfe - how do you use it? and how is passing gulp.src("") to monkeyPatchPipe does anything at all

@yairEO I'm just use

gulp.task('name', function (done) {
  return gulp.src('')
    .pipe(plugin())
    .on('error', done)
    .pipe(plugin()) // Error of the last pipe will be catched by gulp if you return stream.
});

Isn't @robfe's solution basically what gulp-plumber does?

@callmecavs Yup. And it has the same problem with CI

I am using the simple snippet to deliver caught errors in stream but deliver them for gulp: https://gist.github.com/just-boris/89ee7c1829e87e2db04c

It allows to keep watch working on errors and report failures into CI

+1 for installing gulp-plumber

I'm using a variant of https://github.com/gulpjs/gulp/issues/259#issuecomment-63297076 as a workaround

before

Gulp 3.9.0: watch stops permanently on error in default

gulp.task('watch', ['default'], function() {
  return gulp.watch(['src/**/*'], function() {
    return gulp.start('default');
  });
});
after

Gulp 3.9.0: watch recovers after a few seconds and starts listening for new changes

gulp.task('watch-runner', function() {
  return gulp.watch(['src/**/*'], function() {
    return gulp.start('default');
  });
});
gulp.task('watch', ['default'], function() {
  (function spawnWatch() {
    require('child_process').spawn('gulp', ['watch-runner'], {stdio: 'inherit'}).on('close', spawnWatch);
  })();
});

@tomsun Too difficult

@TrySound As an interface promoted by gulp: too difficult indeed :-)

As a workaround if gulp doesn't behave right now (case: closed etc): works for me...
Should earn one simplicity point or two from the fact that you don't have to mess with the error handling of individual tasks. An external actor watching the watcher is less fun, but even simpler - for example in package.json, implementing npm run-script watch as:

 "scripts": {
   "watch": "while true ; do gulp watch ; done"
}

@TrySound not working for me

@babbelnedd What error?

This was raised a year ago and it's still broken?

@phillip-haydon gulp3 won't be fixed. Wait gulp4 release or use it from repo,

For what it's worth, this implementation based on gulp-plumber works well in my build pipeline. It works correctly with a combined-stream2 Duplex stream, and doesn't break watchers either.

still breaking without this.emit('end');

the @mrienstra's advice from this comment helps me

the original answer on stackoverflow

thanks @mikestreety for the this.emit('end') bit! worked for me.

I just followed this thread.
Not sure if I am missing something.

Do I have to choose between "stopping watch on error" and "never fail"

If I implement the 'handleError' - it never fails.. even when it should.
If I don't it stops the process on error.

Or is there a solution to enjoy both worlds?

if you some one uses sass fot gulp plugin ('gulp-sass') and faces with this issue what you need to do is just add an on error event listener to your sass task like this

var sass = require('gulp-sass')

.
.
.

// Compile Sass Files
gulp.task('sass' , () => {
  gulp.src('src/sass/**/*.scss')
    .pipe(wait(250))
    .pipe(sass().on('error' , sass.logError)) // this line here
    .pipe(gulp.dest(BUILD_FOLDER + 'css'))
    .pipe(browserSync.stream())
})

this will prevent from killing server when ever you made a mistake on variables names or things like that ...
hope it helps some one

Was this page helpful?
0 / 5 - 0 ratings