payloadcms/payload · error · TaskError
Inline task with ID ${taskID} does not have a valid handler.
Error message
Inline task with ID ${taskID} does not have a valid handler. What it means
Thrown when an inline task's `task` argument is not a function (or is falsy). The runner assigns `runner = task` for inline tasks, then validates `typeof runner === 'function'`; a non-function inline handler is a programming error in the calling workflow code.
Source
Thrown at packages/payload/src/queues/operations/runJobs/runJob/getRunTaskFunction.ts:114
} else if (typeof finalRetriesConfig?.shouldRestore === 'function') {
shouldRestore = await finalRetriesConfig.shouldRestore({
input,
job,
req,
taskStatus,
})
}
if (shouldRestore) {
return taskStatus.output
}
}
const runner = isInline
? (task as TaskHandler<TaskSlug>)
: await getTaskHandlerFromConfig(taskConfig)
if (!runner || typeof runner !== 'function') {
throw new TaskError({
executedAt,
input,
job,
message: isInline
? `Inline task with ID ${taskID} does not have a valid handler.`
: `Task with slug ${taskSlug} in workflow ${job.workflowSlug} does not have a valid handler.`,
parent,
retriesConfig: finalRetriesConfig,
taskConfig,
taskID,
taskSlug,
taskStatus,
workflowConfig,
})
}
let output: TaskHandlerResult<string>['output']
View on GitHub (pinned to 00c58b35c0)
Solutions
- Pass a function as the `task` argument for inline tasks.
- Verify the handler import: `typeof task === 'function'` before queueing.
- If you meant a configured task, use the task slug path instead of an inline task.
Example fix
// before
await req.tasks('step', { task: MyTaskConfig /* object, not fn */ })
// after
await req.tasks('step', { task: myTaskHandler }) Defensive patterns
Strategy: type-guard
Validate before calling
function isTaskHandler(value: unknown): value is (...args: any[]) => any {
return typeof value === 'function'
}
if (!isTaskHandler(task)) {
throw new Error('inline task handler must be a function')
}
await req.tasks('step', { task }) Type guard
type TaskHandler = (args: any) => Promise<any> | any
function isInlineTaskHandler(value: unknown): value is TaskHandler {
return typeof value === 'function'
}
if (!isInlineTaskHandler(task)) {
throw new TypeError('Expected a function for inline task')
} Try / catch
try {
await req.tasks('step', { task })
} catch (err) {
if (err instanceof TaskError && err.message.includes('does not have a valid handler')) {
// replace the inline task arg with a real function
} else throw err
} Prevention
- Always pass a function (not a config object) as the inline `task` argument.
- Add a unit test asserting `typeof task === 'function'` before queueing.
- Use the slug-based task path when you want config-driven tasks.
When it happens
Trigger: Calling the inline task runner (`req.tasks` / runTask with an inline task) and passing `task` as undefined, an object, or a non-function import — e.g. forgetting to default-export the handler, or passing a config object instead of the function.
Common situations: Importing a handler object instead of its function member; passing `task: undefined` because of an optional that was never set; refactor that changed the inline task signature.
Related errors
- Task ${taskSlug} not found in workflow ${job.workflowSlug}
- Task with slug ${taskSlug} in workflow ${job.workflowSlug} d
- Task handler threw an error
- Error importing job queue handler module for path ${path}. T
- `jobs.processingLease.safetyBuffer` must be non-negative and
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/c3a3c41e54c4922f.
Report an issue: GitHub.