Skip to content

Cancelling jobs

Cancel a job when it should never run again, or request cancellation of an execution already in progress. A cancelled job is not retried automatically.


Cancelling from within a worker

Ordinary worker errors are retried, on the assumption that the problem is temporary or can be fixed by a code deploy. When a worker detects a permanent failure, such as an account that has been closed, it can cancel the job instead.

Return river.JobCancel from the worker’s Work method:

func (w *CancellingWorker) Work(ctx context.Context, j *river.Job[CancellingArgs]) error {
return river.JobCancel(fmt.Errorf("account has been closed"))
}

The wrapped error is persisted in the job’s error history, so operators can see why it was cancelled. See the JobCancel example for complete code.

Cancelling a job from the client

Use a job’s ID to cancel it from a client, whether or not it is currently running. For an existing job, each method returns its current record; a running job may still be marked running while its worker responds to the request.

Call Client.JobCancel:

if _, err := riverClient.JobCancel(ctx, jobID); err != nil {
// handle error
}

See the JobCancelFromClient example for complete code.

The result of cancellation depends on the job’s state and whether its worker finishes before the request takes effect. Cancellation does not undo work or external side effects that have already happened.

Jobs still running

Running jobs are marked for cancellation rather than immediately finalized. The client executing the job then signals or interrupts its worker. A worker can finish before this happens, and how it responds to the request depends on its language.

River cancels the worker’s context.Context. Go cannot interrupt a running goroutine, so the worker must respect context cancellation and return promptly. Pass ctx to database and HTTP calls, and check ctx.Done() in long-running loops or waits.

After a remote cancellation signal is received, returning an error cancels the job permanently. Returning nil still completes the job successfully.

If the worker finishes after the cancellation request is recorded but before it receives the signal:

  • If the job was due to be finalized because it completed successfully, was cancelled from within, or was discarded due to exceeding its max attempts, the job will be updated as usual.
  • If the job was snoozed to run again later or encountered a retryable error, the job will be marked as cancelled and will not be attempted again.

Jobs still enqueued

For jobs that have not started (available, scheduled, retryable, or pending), cancellation is straightforward. The job is immediately and atomically marked as cancelled so that no client will fetch it from the queue and it will not be attempted again. If a worker claims the job first, the running-job behavior above applies instead.

Jobs already finalized

If the job is already finalized (cancelled, completed, or discarded), no changes are made.

Cancelling a job from the UI

The River UI enables cancelling jobs from the web interface. Cancellation can be performed in bulk from the job list page on selected jobs, or individually from the job details page.