payloadcms/payload · error · TaskError
Task with slug ${taskSlug} in workflow ${job.workflowSlug} d
Error message
Task with slug ${taskSlug} in workflow ${job.workflowSlug} does not have a valid handler. What it means
Thrown when a configured (non-inline) task's handler cannot be resolved to a function. The runner calls `getTaskHandlerFromConfig(taskConfig)`; if that returns falsy or a non-function, the task has no executable handler — typically because neither a `handler` function nor a resolvable `handler.path` was defined.
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
- Add `handler: async (args) => {...}` to the task config.
- Or set `handler: { path: './tasks/foo.js' }` and ensure that module has a default or named export.
- Verify `getTaskHandlerFromConfig` can resolve it by importing the module manually in a test.
Example fix
// before
tasks: [{ slug: 'resize', inputSchema, /* no handler */ }]
// after
tasks: [{ slug: 'resize', inputSchema, handler: async ({ input }) => { /* ... */ } }] Defensive patterns
Strategy: validation
Validate before calling
import type { TaskConfig } from 'payload'
function assertTaskHasHandler(task: TaskConfig): void {
const hasFn = typeof task.handler === 'function'
const hasPath =
!!task.handler && typeof task.handler === 'object' && 'path' in (task.handler as object)
if (!hasFn && !hasPath) {
throw new Error(`Task "${task.slug}" has no handler function or path`)
}
}
(tasks ?? []).forEach(assertTaskHasHandler) Type guard
function hasHandler(task: { handler?: unknown }): boolean {
return typeof task.handler === 'function' ||
(!!task.handler && typeof task.handler === 'object' && 'path' in (task.handler as object))
}
if (!hasHandler(taskConfig)) throw new Error('task missing handler') Try / catch
try {
await runJobs({ req })
} catch (err) {
if (err instanceof Error && err.message.includes('does not have a valid handler')) {
// add handler to the task config or fix the handler.path export
} else throw err
} Prevention
- Add a config-build test asserting every task has a function or path handler.
- When using handler.path, verify the module's default/named export resolves.
- Keep handler imports explicit functions rather than paths where possible.
When it happens
Trigger: A task in `jobs.tasks` / a workflow's tasks has no `handler` and no valid `handler.path`, or the path resolves to a module without a default/named export.
Common situations: Defining a task config with only a slug and input/output schema but forgetting the handler; handler path string pointing to a file that has no matching export; refactor removed the handler function.
Related errors
- Task ${taskSlug} not found in workflow ${job.workflowSlug}
- Task handler threw an error
- Error importing job queue handler module for path ${path}. T
- Task slug "${task.slug}" is already used by a workflow. No t
- Inline task with ID ${taskID} does not have a valid handler.
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/1a18ed853633a819.
Report an issue: GitHub.