payloadcms/payload · error · TaskError
Task handler threw an error
Error message
Task handler threw an error
What it means
Thrown as a wrapped `TaskError` when the task handler function throws during execution. The runner catches any non-cancellation error (`JobCancelledError`/`JobRunAbortedError` are rethrown) and re-emits it as a TaskError carrying the original `err.message`, plus task/job context, input, and the resolved retries config. This is the user-facing surface of a handler bug.
Source
Thrown at packages/payload/src/queues/operations/runJobs/runJob/getRunTaskFunction.ts:154
inlineTask: getRunTaskFunction(job, workflowConfig, req, true, updateJob, {
taskID,
taskSlug,
}),
input,
job: job as unknown as Job<WorkflowSlug>,
req,
tasks: getRunTaskFunction(job, workflowConfig, req, false, updateJob, {
taskID,
taskSlug,
}),
})
)?.output
} catch (err: any) {
if (err instanceof JobCancelledError || err instanceof JobRunAbortedError) {
// Job run aborts are handled by the top-level runner.
throw err
}
throw new TaskError({
executedAt,
input: input!,
job,
message: err.message || 'Task handler threw an error',
output,
parent,
retriesConfig: finalRetriesConfig,
taskConfig,
taskID,
taskSlug,
taskStatus,
workflowConfig,
})
}
if (taskConfig?.onSuccess) {
await taskConfig.onSuccess({
input,View on GitHub (pinned to 00c58b35c0)
Solutions
- Inspect the TaskError's `message` and `output` (often the original error) to find the root cause.
- Fix the handler's failure mode; add input validation and defensive checks.
- Configure task `retries` so transient handler failures are retried instead of failing the job.
Example fix
// before
handler: async ({ input }) => {
return JSON.parse(input.body) // throws on bad input
}
// after
handler: async ({ input }) => {
try {
return JSON.parse(input.body)
} catch {
throw new Error(`Invalid JSON in task input: ${input.body}`)
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// make handlers defensive and validate inputs
handler: async ({ input }) => {
if (!input || typeof input.body !== 'string') {
throw new Error('Task input.body is required and must be a string')
}
return JSON.parse(input.body)
} Type guard
function isTaskError(err: unknown): err is { message: string; taskSlug?: string } {
return err instanceof Error && 'taskConfig' in (err as object)
} Try / catch
try {
await runJobs({ req })
} catch (err) {
if (isTaskError(err)) {
// inspect err.message + err.output; fix handler or bump task.retries
logger.error({ task: err.taskSlug, msg: err.message })
}
throw err
} Prevention
- Validate task inputs at the top of the handler before touching fields.
- Configure task.retries so transient failures retry instead of failing the job.
- Log handler errors with structured context for debugging.
When it happens
Trigger: The task handler threw — null deref, failed DB write, rejected fetch, business-logic exception, etc. The error propagates with the handler's message (or 'Task handler threw an error' if the thrown value had no message).
Common situations: Handler calls an external API that returned an error; null/undefined input access; a DB constraint violation inside the task; downstream service outage.
Related errors
- Task with slug ${taskSlug} in workflow ${job.workflowSlug} d
- Error importing job queue handler module for path ${path}. T
- Task ${taskSlug} not found in workflow ${job.workflowSlug}
- Inline task with ID ${taskID} does not have a valid handler.
- `jobs.processingLease.safetyBuffer` must be non-negative and
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/ba158657a028e704.
Report an issue: GitHub.