Snoozing allows a worker to try a job again at a later time without counting the attempt as a failure. The job will be put back into the queue and scheduled to run again after the specified duration.
Snoozing a job
Under normal circumstances, jobs that return an error are scheduled to be retried. This is done under the assumption that the problem they ran into was intermittent, or can be corrected by a code deploy that fixes a worker bug. However, sometimes a worker might want to execute the same job again in the future, even if no error has occurred.
While this could be achieved by enqueueing a new job, doing so can result in having many separate jobs in the database. The other option is to return an error from the job, but that doesn’t offer control over the retry interval and can eventually cause the job to exhaust its maximum attempts. Snoozing avoids these issues.
To snooze a job for later execution, return the result of JobSnooze from a worker:
func (w *SnoozingWorker) Work(ctx context.Context, j *river.Job[SnoozingArgs]) error { if tryAgainLater { return river.JobSnooze(30*time.Second) } return nil}See the JobSnooze example for complete code.
JobSnooze takes one argument — the duration (after time.Now()) to snooze until the job should be attempted again. It returns an error that River will recognize as a signal to snooze the job (rather than counting it as an actual error).
Raise River.job_snooze with a duration in seconds:
def work(job) raise River.job_snooze(30) if try_again_later?endReturn a snooze outcome:
use std::time::Duration;use riverqueue::{BoxError, Job, WorkContext, WorkOutcome};
async fn work(_context: WorkContext, _job: Job<SnoozingArgs>) -> Result<WorkOutcome, BoxError> { if try_again_later() { return Ok(WorkOutcome::Snooze(Duration::from_secs(30))); } Ok(WorkOutcome::Complete)}Return snooze from the handler when the job should try again later. This example requests a thirty-second delay:
import { snooze } from "riverqueue";
workers.add(snoozingJob, async ({ job }) => { if (await tryAgainLater(job)) return snooze({ seconds: 30 });});The job becomes eligible to run again after the snooze duration. Short snoozes can be fetched directly when due; longer snoozes pass through the scheduler. Polling, maintenance intervals, and worker capacity can add a delay.
No impact on retries
Snoozing is an intentional decision by the worker to try a job again later and isn’t considered an error. By extension, snoozing does not affect a job’s retry behavior. Each time a job is snoozed, its attempt count is decremented by one so that a job can keep snoozing indefinitely without exhausting its maximum attempts. If a snoozed job later encounters an error and requires a retry, the previous snoozes will not be counted as errors when determining the retry backoff duration.
Metadata
When a job is snoozed, a counter is incremented in the "snoozes" metadata field:
{ "snoozes": 1 }