What about this combination of gulp-concat and lazypipe is causing an error using gulp 4?

This is a known issue when using lazypipe with gulp 4 and it's not going to be fixed in the near future. Quote from that issue:

OverZealous commented on 20 Dec 2015
As of now, I have no intention of making lazypipe work on Gulp 4.

As far as I can tell this issue is caused by the fact that gulp 4 uses async-done which has this to say about its stream support:

Note: Only actual streams are supported, not faux-streams; Therefore, modules like event-stream are not supported.

When you use lazypipe() as the last pipe what you get is a stream that doesn't have a lot of the properties that you usually have when working with streams in gulp. You can see this for yourself by logging the streams:

// console output shows lots of properties
console.log(gulp.src(src('js/**/*.js'))
  .pipe(plugins.concat('cat.js'))
  .pipe(gulp.dest(dest))); 

// console output shows much fewer properties
console.log(gulp.src(src('js/**/*.js'))
  .pipe(buildFiles())); 

This is probably the reason why gulp considers the second stream to be a "faux-stream" and doesn't properly detect when the stream has finished.

Your only option at this point is some kind of workaround. The easiest workaround (which doesn't require any additional packages) is to just add a callback function cb to your task and listen for the 'end' event:

gulp.task('build', function(cb) {
  var dest = 'build';

  var buildFiles = lazypipe()
   .pipe(plugins.concat, 'cat.js') 
   .pipe(gulp.dest, dest);

  gulp.src(src('js/**/*.js'))
   .pipe(buildFiles())
   .on('end', cb);
});

Alternatively, adding any .pipe() after buildFiles() should fix this, even one that doesn't actually do anything like gutil.noop():

var gutil = require('gulp-util');

gulp.task('build', function() {
  var dest = 'build';

  var buildFiles = lazypipe()
    .pipe(plugins.concat, 'cat.js') 
    .pipe(gulp.dest, dest);

  return gulp.src(src('js/**/*.js'))
    .pipe(buildFiles())
    .pipe(gutil.noop());
});