{"record":{"id":"1a50acc55d1334a2","repo":"angular/angular","slug":"task-is-missing-schedulefn","errorCode":null,"errorMessage":"Task is missing scheduleFn.","messagePattern":"Task is missing scheduleFn\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zone.js/lib/zone-impl.ts","lineNumber":1313,"sourceCode":"      let returnTask: ZoneTask<any> = task as ZoneTask<any>;\n      if (this._scheduleTaskZS) {\n        if (this._hasTaskZS) {\n          returnTask._zoneDelegates!.push(this._hasTaskDlgtOwner!);\n        }\n        returnTask = this._scheduleTaskZS.onScheduleTask!(\n          this._scheduleTaskDlgt!,\n          this._scheduleTaskCurrZone!,\n          targetZone,\n          task,\n        ) as ZoneTask<any>;\n        if (!returnTask) returnTask = task as ZoneTask<any>;\n      } else {\n        if (task.scheduleFn) {\n          task.scheduleFn(task);\n        } else if (task.type == microTask) {\n          scheduleMicroTask(<MicroTask>task);\n        } else {\n          throw new Error('Task is missing scheduleFn.');\n        }\n      }\n      return returnTask;\n    }\n\n    invokeTask(targetZone: ZoneImpl, task: Task, applyThis: any, applyArgs?: any[]): any {\n      return this._invokeTaskZS\n        ? this._invokeTaskZS.onInvokeTask!(\n            this._invokeTaskDlgt!,\n            this._invokeTaskCurrZone!,\n            targetZone,\n            task,\n            applyThis,\n            applyArgs,\n          )\n        : task.callback.apply(applyThis, applyArgs);\n    }\n","sourceCodeStart":1295,"sourceCodeEnd":1331,"githubUrl":"https://github.com/angular/angular/blob/51cb07e98081ab7e4e84a9e0949266a8e19cca84/packages/zone.js/lib/zone-impl.ts#L1295-L1331","documentation":"When a task is scheduled through a zone delegate chain that does not handle scheduling itself, zone.js falls back to task.scheduleFn; for microTasks it drains the microtask queue instead. If the task has no scheduleFn and is not a microTask (i.e. macroTask or eventTask), there is no way to actually start it, so scheduleTask throws 'Task is missing scheduleFn.' This almost always means a raw Task object was passed to scheduleTask instead of being created via the helper APIs.","triggerScenarios":"Constructing a task manually (new (Zone as any).Task(...) or a hand-rolled object) and calling zone.scheduleTask(task) with scheduleFn unset; copying the Task shape from documentation but omitting the scheduleFn argument; calling scheduleEventTask/scheduleMacroTask with both customSchedule undefined while the delegate chain is empty — the normal helpers set scheduleFn, so hitting this usually means bypassing them.","commonSituations":"Library authors integrating custom async primitives (IPC, worker messages, custom timers) with zone.js; adapting old zone.js example code to newer typings; partially mocked Zone objects in tests where scheduleFn was stubbed away.","solutions":["Use Zone.current.scheduleMacroTask(source, callback, data, scheduleFn, cancelFn) or scheduleEventTask(...) which wire scheduleFn correctly","If building a Task by hand, supply the third-party scheduling step as scheduleFn: task.scheduleFn = (t) => { t.data!.handle = nativeSchedule(t.invoke) }","For fire-and-forget microtasks, use Zone.current.scheduleMicroTask(source, callback, data) instead"],"exampleFix":"// before\nconst task = {type: 'macroTask', source: 'ipc', callback: onMsg} as any;\nZone.current.scheduleTask(task); // no scheduleFn -> throws\n\n// after\nconst task = Zone.current.scheduleMacroTask('ipc', onMsg, null, (t) => {\n  t.data = {...(t.data||{}), handle: ipcPort.once('msg', t.invoke)};\n}, (t) => ipcPort.removeListener('msg', t.invoke));","handlingStrategy":"validation","validationCode":"// never hand-build tasks; use the helper that sets scheduleFn\nconst task = Zone.current.scheduleMacroTask(\n  'my-source', cb, null,\n  (t) => { t.data = {handle: nativeSchedule(t.invoke)}; },\n  (t) => nativeCancel(t.data!.handle),\n);","typeGuard":"const isSchedulableTask = (t: any): boolean =>\n  typeof t.scheduleFn === 'function' || t.type === 'microTask';","tryCatchPattern":null,"preventionTips":["Use scheduleMacroTask/scheduleEventTask/scheduleMicroTask instead of constructing Task objects manually","If a Task-like object is required, always assign scheduleFn and cancelFn before scheduleTask"],"tags":["zone-js","task","scheduler","api-misuse"],"backgroundTag":"task-schedule-misuse","analyzedSha":"51cb07e98081ab7e4e84a9e0949266a8e19cca84","analyzedAt":"2026-08-22T07:04:54.531Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}