From 6d0007c3ddfcd4ad090ac99ca3bb197e0a21c015 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Thu, 21 May 2026 01:46:09 -0700 Subject: [PATCH] fix: record error message and backtrace when job expires --- lib/que/job_methods.rb | 18 ++++++++++++++---- lib/que/worker.rb | 6 +++++- spec/que/worker_spec.rb | 13 +++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/que/job_methods.rb b/lib/que/job_methods.rb index 0e07b2c4..02c3ef6b 100644 --- a/lib/que/job_methods.rb +++ b/lib/que/job_methods.rb @@ -11,9 +11,11 @@ module Que SQL[:expire_job] = %{ UPDATE public.que_jobs - SET error_count = error_count + 1, - expired_at = now() - WHERE id = $1::bigint + SET error_count = error_count + 1, + expired_at = now(), + last_error_message = COALESCE(left($1::text, 500), last_error_message), + last_error_backtrace = COALESCE(left($2::text, 10000), last_error_backtrace) + WHERE id = $3::bigint } SQL[:destroy_job] = @@ -101,7 +103,15 @@ def expire return unless que_target if id = que_target.que_attrs[:id] - Que.execute :expire_job, [id] + if e = que_target.que_error + message = "#{e.class}: #{e.message}".slice(0, 500) + backtrace = (e.backtrace || []).join("\n").slice(0, 10000) + else + message = nil + backtrace = nil + end + + Que.execute :expire_job, [message, backtrace, id] end que_target.que_resolved = true diff --git a/lib/que/worker.rb b/lib/que/worker.rb index 00ef19aa..aeb5ae3d 100644 --- a/lib/que/worker.rb +++ b/lib/que/worker.rb @@ -153,7 +153,11 @@ def work_job(metajob) max_retry_count = job_class.resolve_que_setting(:maximum_retry_count) if max_retry_count && error_count > max_retry_count - Que.execute :expire_job, [job.fetch(:id)] + Que.execute :expire_job, [ + "#{error.class}: #{error.message}".slice(0, 500), + (error.backtrace || []).join("\n").slice(0, 10000), + job.fetch(:id), + ] else delay = job_class. diff --git a/spec/que/worker_spec.rb b/spec/que/worker_spec.rb index c5851701..4df502a1 100644 --- a/spec/que/worker_spec.rb +++ b/spec/que/worker_spec.rb @@ -354,6 +354,19 @@ def run(*args) end end end + + it "should record the error message and backtrace on the expired job" do + WorkerJob.maximum_retry_count = 0 + job = WorkerJob.enqueue + ds = jobs_dataset.where(id: job.que_attrs[:id]) + + run_jobs + + row = ds.first + assert_in_delta row[:expired_at], Time.now, QueSpec::TIME_SKEW + assert_equal "RuntimeError: Blah!", row[:last_error_message] + assert_match(/\A#{__FILE__}/, row[:last_error_backtrace].split("\n").first) + end end end