@@ -108,6 +108,33 @@ export async function GET(request: NextRequest) {
108108 } )
109109 }
110110
111+ // Clean up stale pending jobs (never started, e.g., due to server crash before startJob())
112+ let stalePendingJobsMarkedFailed = 0
113+
114+ try {
115+ const stalePendingJobs = await db
116+ . update ( asyncJobs )
117+ . set ( {
118+ status : JOB_STATUS . FAILED ,
119+ completedAt : new Date ( ) ,
120+ error : `Job terminated: stuck in pending state for more than ${ STALE_THRESHOLD_MINUTES } minutes (never started)` ,
121+ updatedAt : new Date ( ) ,
122+ } )
123+ . where (
124+ and ( eq ( asyncJobs . status , JOB_STATUS . PENDING ) , lt ( asyncJobs . createdAt , staleThreshold ) )
125+ )
126+ . returning ( { id : asyncJobs . id } )
127+
128+ stalePendingJobsMarkedFailed = stalePendingJobs . length
129+ if ( stalePendingJobsMarkedFailed > 0 ) {
130+ logger . info ( `Marked ${ stalePendingJobsMarkedFailed } stale pending jobs as failed` )
131+ }
132+ } catch ( error ) {
133+ logger . error ( 'Failed to clean up stale pending jobs:' , {
134+ error : error instanceof Error ? error . message : String ( error ) ,
135+ } )
136+ }
137+
111138 // Delete completed/failed jobs older than retention period
112139 const retentionThreshold = new Date ( Date . now ( ) - JOB_RETENTION_HOURS * 60 * 60 * 1000 )
113140 let asyncJobsDeleted = 0
@@ -144,7 +171,8 @@ export async function GET(request: NextRequest) {
144171 thresholdMinutes : STALE_THRESHOLD_MINUTES ,
145172 } ,
146173 asyncJobs : {
147- staleMarkedFailed : asyncJobsMarkedFailed ,
174+ staleProcessingMarkedFailed : asyncJobsMarkedFailed ,
175+ stalePendingMarkedFailed : stalePendingJobsMarkedFailed ,
148176 oldDeleted : asyncJobsDeleted ,
149177 staleThresholdMinutes : STALE_THRESHOLD_MINUTES ,
150178 retentionHours : JOB_RETENTION_HOURS ,
0 commit comments