{"record":{"id":"f019125e573b2c24","repo":"gulpjs/gulp","slug":"watching-glob-watch-task-has-to-be-a-function","errorCode":null,"errorMessage":"watching ${glob}: watch task has to be a function (optionally generated by using gulp.parallel or gulp.series)","messagePattern":"watching (.+?): watch task has to be a function \\(optionally generated by using gulp\\.parallel or gulp\\.series\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"index.js","lineNumber":31,"sourceCode":"  this.task = this.task.bind(this);\n  this.series = this.series.bind(this);\n  this.parallel = this.parallel.bind(this);\n  this.registry = this.registry.bind(this);\n  this.tree = this.tree.bind(this);\n  this.lastRun = this.lastRun.bind(this);\n  this.src = this.src.bind(this);\n  this.dest = this.dest.bind(this);\n  this.symlink = this.symlink.bind(this);\n}\nutil.inherits(Gulp, Undertaker);\n\nGulp.prototype.src = vfs.src;\nGulp.prototype.dest = vfs.dest;\nGulp.prototype.symlink = vfs.symlink;\nGulp.prototype.watch = function(glob, opt, task) {\n  if (typeof opt === 'string' || typeof task === 'string' ||\n    Array.isArray(opt) || Array.isArray(task)) {\n    throw new Error('watching ' + glob + ': watch task has to be ' +\n      'a function (optionally generated by using gulp.parallel ' +\n      'or gulp.series)');\n  }\n\n  if (typeof opt === 'function') {\n    task = opt;\n    opt = {};\n  }\n\n  opt = opt || {};\n\n  var fn;\n  if (typeof task === 'function') {\n    fn = this.parallel(task);\n  }\n\n  return watch(glob, opt, fn);\n};","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/gulpjs/gulp/blob/61f22dc11bb14234b555253095fa1d224ce0eab1/index.js#L13-L49","documentation":"This error is thrown by Gulp 4's gulp.watch() when the opt or task argument is a string or an array (checked at index.js:29-34). Gulp 3 let you pass task-name strings or arrays of task names to gulp.watch(); Gulp 4 removed that contract — the task argument must be a real function, optionally composed with gulp.parallel or gulp.series. The guard fires before delegation to the underlying watch() call so that invalid (legacy-style) task specifiers fail loudly instead of silently doing nothing.","triggerScenarios":"Calling gulp.watch(glob, ['task1','task2']) (array as 2nd arg), gulp.watch(glob, 'task') (string as 2nd arg), gulp.watch(glob, options, 'task') (string as 3rd arg), or gulp.watch(glob, options, ['task1','task2']) (array as 3rd arg). Any value where typeof opt === 'string' || typeof task === 'string' || Array.isArray(opt) || Array.isArray(task) trips the throw at index.js:29-30.","commonSituations":"Migrating a gulpfile from gulp 3.x to 4.x without updating watch() calls; copying watch examples from a blog/tutorial written for gulp 3; mixing gulp.task('name', fn) named-task style with watch() by passing the name string; a version mismatch between locally installed gulp 4 and a globally installed gulp 3 CLI (or vice versa) producing inconsistent docs/API expectations.","solutions":["Replace string/array task names with function references composed via gulp.parallel() or gulp.series(), e.g. gulp.watch('src/**/*.js', gulp.parallel(lint, build)).","If tasks are registered by name through gulp.task('name', fn), pass them through gulp.series('name') or gulp.parallel('name') — Undertaker resolves named strings inside these composers, but NOT as the raw task argument to watch().","Check the installed version with npm ls gulp and confirm it is 4.x; align your gulpfile and any copied examples to the 4.x watch() API.","Refactor named gulp.task() definitions into plain function declarations (function lint(){...}) and reference the function directly in watch(), which is the idiomatic Gulp 4 pattern."],"exampleFix":"// before (gulp 3.x style — throws in gulp 4)\ngulp.watch('src/**/*.js', ['lint', 'build']);\ngulp.watch('src/**/*.js', 'build');\n\n// after (gulp 4.x)\ngulp.watch('src/**/*.js', gulp.parallel(lint, build));\ngulp.watch('src/**/*.js', build);\n// or, when tasks are registered by name:\ngulp.watch('src/**/*.js', gulp.series('lint', 'build'));","handlingStrategy":"validation","validationCode":"function isWatchTaskValid(task) {\n  return task == null || typeof task === 'function';\n}\n\n// before calling watch:\nif (typeof opt === 'string' || Array.isArray(opt) || typeof task === 'string' || Array.isArray(task)) {\n  throw new TypeError('gulp.watch: opt/task must be a function or options object, not a string/array');\n}\ngulp.watch(glob, opt, task);","typeGuard":"/**\n * Narrows a value to the gulp 4 watch() task contract:\n * must be a function (or undefined). Rejects the gulp 3\n * string/array task-name forms.\n */\nfunction isGulpWatchTask(value) {\n  return typeof value === 'function';\n}\n\n// usage\nif (isGulpWatchTask(myTask)) {\n  gulp.watch('src/**/*.js', myTask);\n} else if (Array.isArray(myTask) || typeof myTask === 'string') {\n  // legacy gulp 3 form — convert with gulp.parallel/gulp.series\n  gulp.watch('src/**/*.js', gulp.parallel.apply(null, [].concat(myTask)));\n}","tryCatchPattern":"// Generally prefer validation over try/catch here — the error is a\n// static API misuse, not a runtime/environment failure. If wrapping\n// (e.g. a config loader that reads task names from disk):\ntry {\n  gulp.watch(glob, task);\n} catch (err) {\n  if (/watch task has to be a function/.test(err.message)) {\n    console.error('gulp.watch: passed a task name/array but gulp 4 needs a function —',\n      'wrap with gulp.parallel() or gulp.series(). Got:', task);\n    // rethrow or fall back to a composed task built from names\n    gulp.watch(glob, gulp.series.apply(gulp, [].concat(task)));\n  } else {\n    throw err;\n  }\n}","preventionTips":["Pin your gulp major version (gulp 3 vs 4) and read watch() docs/examples matching that exact version before writing gulpfile watch calls.","Prefer function declarations (function lint(){...}) over gulp.task('lint', fn) so watch() always receives a function reference — no name-string ambiguity.","Run a CI lint step that greps the gulpfile for gulp.watch(.*,\\s*['\\[] to catch legacy string/array task arguments before they ship.","If migrating gulp 3 → 4, search the gulpfile for watch( calls and convert every string or array task argument to gulp.parallel()/gulp.series() in one pass.","Keep a wrapper helper, e.g. function watchTasks(glob, tasks){ return gulp.watch(glob, gulp.parallel.apply(gulp, [].concat(tasks))); } to centralize the API contract."],"tags":["gulp","watch","migration","gulpfile","task","api-change","gulp4"],"backgroundTag":null,"analyzedSha":"61f22dc11bb14234b555253095fa1d224ce0eab1","analyzedAt":"2026-08-13T04:40:47.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}