Skip to content

[copilot-finds] Bug: createTimer() silently accepts NaN, Infinity, and invalid Date inputs, producing timers that fire at wrong times #220

@github-actions

Description

@github-actions

Problem

RuntimeOrchestrationContext.createTimer() (packages/durabletask-js/src/worker/runtime-orchestration-context.ts, line 296) accepts a number | Date parameter but performs no validation on the input value. When non-finite numbers (NaN, Infinity, -Infinity) or invalid Date objects are passed, the method silently creates timers with corrupted timestamps instead of reporting the error.

Example: ctx.createTimer(parseInt("abc"))NaNnew Date(NaN) → protobuf Timestamp with seconds=0 (Unix epoch) → timer fires immediately instead of failing.

Specific cases:

  • ctx.createTimer(NaN) → timer fires at Unix epoch (1970-01-01) — silently wrong
  • ctx.createTimer(Infinity) → timer fires at Unix epoch — silently wrong
  • ctx.createTimer(new Date("not a date")) → timer fires at Unix epoch — silently wrong

Root Cause

The createTimer method converts number inputs to Date objects via new Date(this._currentUtcDatetime.getTime() + fireAt * 1000) but never validates that fireAt is a finite number. When fireAt is NaN or Infinity, the resulting Date is invalid. The protobuf Timestamp.fromDate() method then converts NaN timestamps to 0 (epoch) during serialization, producing a timer that fires immediately.

Similarly, passing an invalid Date object bypasses the number conversion path (since instanceof Date is true) but produces the same corrupted timestamp.

The nearby createRetryTimer method (line 536) does validate its delay parameter (if (delayMs <= 0) throw Error), making this an inconsistency within the same class.

Proposed Fix

Add input validation to createTimer():

  1. For number inputs: verify typeof === "number" and Number.isFinite(fireAt), throwing a descriptive error for NaN/Infinity
  2. For Date inputs: verify !isNaN(date.getTime()), throwing a descriptive error for invalid Date objects
  3. Add unit tests covering all invalid and valid input combinations

Impact

Severity: Medium-High. Orchestrator authors who compute timer delays from external data (e.g., parseInt(userInput)) or construct Date objects from strings can silently get timers that fire at the wrong time. This causes:

  • Retry delays not being applied (immediate retry instead of backoff)
  • Rate-limiting timers being skipped
  • Scheduled operations executing immediately instead of at the intended time

The error is silent — no exception is thrown, no log is emitted — making it very difficult to diagnose.

Metadata

Metadata

Assignees

No one assigned

    Labels

    copilot-findsFindings from daily automated code review agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions