From 09b5a444d34651c50a64d86bd91673fb0abb031e Mon Sep 17 00:00:00 2001 From: "molecule-ai[bot]" <276602405+molecule-ai[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:08:00 +0000 Subject: [PATCH] fix(scheduler): use context.Background() in panic-recovery defer UPDATE (F1089) (#1211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F1089: PR #1032's panic-recovery defers used the outer `ctx` passed into fireSchedule/tick. If that ctx was cancelled during the panic window (HTTP timeout, graceful shutdown), ExecContext returned early and the next_run_at UPDATE was silently skipped — leaving the schedule stuck. Fix: both panic defers now call ExecContext(context.Background()) so the recovery UPDATE is independent of the outer ctx's lifecycle. Refs: #1201 (F1089, security audit 2026-04-21) Co-authored-by: Molecule AI CP-BE --- workspace-server/internal/scheduler/scheduler.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/workspace-server/internal/scheduler/scheduler.go b/workspace-server/internal/scheduler/scheduler.go index d74baa39..70c7e0aa 100644 --- a/workspace-server/internal/scheduler/scheduler.go +++ b/workspace-server/internal/scheduler/scheduler.go @@ -215,7 +215,9 @@ func (s *Scheduler) tick(ctx context.Context) { // Always advance next_run_at even on panic so the schedule doesn't get // stuck re-firing the same panicking schedule indefinitely (#1029). if nextTime, err := ComputeNextRun(s2.CronExpr, s2.Timezone, time.Now()); err == nil { - db.DB.ExecContext(ctx, `UPDATE workspace_schedules SET next_run_at=$1, updated_at=now() WHERE id=$2`, nextTime, s2.ID) + // F1089: use context.Background() so the panic-recovery UPDATE is not + // silently skipped if the outer ctx was cancelled during the panic window. + db.DB.ExecContext(context.Background(), `UPDATE workspace_schedules SET next_run_at=$1, updated_at=now() WHERE id=$2`, nextTime, s2.ID) } } }() @@ -246,8 +248,10 @@ func (s *Scheduler) fireSchedule(ctx context.Context, sched scheduleRow) { // Always advance next_run_at even on panic so the schedule doesn't get // stuck re-firing the same panicking schedule indefinitely (#1029). if nextTime, err := ComputeNextRun(sched.CronExpr, sched.Timezone, time.Now()); err == nil { - db.DB.ExecContext(ctx, `UPDATE workspace_schedules SET next_run_at=$1, updated_at=now() WHERE id=$2`, nextTime, sched.ID) - } + // F1089: use context.Background() so the panic-recovery UPDATE is not + // silently skipped if the outer ctx was cancelled during the panic window. + db.DB.ExecContext(context.Background(), `UPDATE workspace_schedules SET next_run_at=$1, updated_at=now() WHERE id=$2`, nextTime, sched.ID) + } } }()