One-shot Codex automation silently scheduled one year late due to timezone-ambiguous RRULE
What version of the Codex App are you using (From “About Codex” dialog)?
26.727.51351 build 6119
What subscription do you have?
PRO X20
What platform is your computer?
macOS: 26.5.2 build 25F84
What issue are you seeing?
-
What steps can reproduce the bug?
-
What is the expected behavior?
The automation was created at:
2026-08-01T06:09:06Z
It was intended to run at:
2026-08-01T06:17:03Z
2026-08-01 02:17:03 America/Santiago
Submitted schedule
RRULE:FREQ=YEARLY;COUNT=1;BYMONTH=8;BYMONTHDAY=1;BYHOUR=2;BYMINUTE=17;BYSECOND=3
The rule contained neither an explicit TZID nor an explicit DTSTART.
Additional information
Summary
A critical heartbeat automation was created successfully in Codex Desktop, but it never ran at the requested time. The automation was intended to wake a task eight minutes later. Instead, its next execution was silently scheduled for August 1, 2027.
This was not a scheduler crash. The scheduler followed the persisted schedule. The immediate caller error was constructing a timezone-less RRULE using local clock fields. However, the automation API made this failure unsafe and difficult to detect: it accepted the rule, returned a generic success response, and did not expose the normalized schedule or computed nextRunAt.
Environment
- Codex Desktop:
26.727.51351, build6119 - Codex CLI:
0.146.0-alpha.9.2 - macOS:
26.5.2build25F84 - Local timezone:
America/Santiago, UTC−04:00
Expected behavior
The automation was created at:
2026-08-01T06:09:06Z
It was intended to run at:
2026-08-01T06:17:03Z
2026-08-01 02:17:03 America/Santiago
Submitted schedule
RRULE:FREQ=YEARLY;COUNT=1;BYMONTH=8;BYMONTHDAY=1;BYHOUR=2;BYMINUTE=17;BYSECOND=3
The rule contained neither an explicit TZID nor an explicit DTSTART.
Actual behavior
The API returned only:
Created automation in the app.
The persisted automation state was:
status: ACTIVE
next_run_at: 1817086623000
next_run_utc: 2027-08-01 02:17:03
last_run_at: NULL
The automation had zero execution records. The application was running at the intended time, but no dispatcher or executor attempt occurred because the stored schedule was not due until 2027.
Root cause
The direct cause was a timezone mismatch:
- The caller encoded the local hour,
02:17, in an RRULE withoutTZID. - With no explicit
DTSTART, Codex supplied a start point and interpreted the calendar fields against UTC. 02:17Zon August 1, 2026 was already in the past when the automation was created at06:09Z.- Because the rule was yearly and limited to one occurrence, the next valid future occurrence became August 1, 2027.
The primary construction mistake was therefore on the agent side: a local wall-clock time was placed into a timezone-ambiguous rule.
The product/API amplified that mistake into a silent reliability failure. Creation succeeded without returning the canonical schedule, timezone, or calculated next execution time. The caller had no postcondition by which to verify that the requested and persisted execution times matched.
Impact
The automation was guarding an external review boundary in an autonomous engineering workflow. The false success signal caused the workflow to remain idle for approximately nine hours despite the required external approval already being available.
This failure mode is particularly dangerous for autonomous workflows because:
- the automation appears active;
- no error is emitted;
- no missed-run notification occurs;
- the task can remain unattended indefinitely;
- the actual next run may be months or years away.
Recommended changes
- Return the normalized
nextRunAt, effective timezone, and canonicalDTSTARTfrom every create and update operation.
- Support an
expectedRunAtpostcondition. Creation should fail if the calculated execution time differs from the requested instant beyond a small tolerance.
- Require an explicit timezone or UTC
DTSTARTfor one-shot calendar schedules containingBYHOUR.
- Warn or fail when a newly created one-shot automation resolves far beyond the requested or creation time.
- Treat
COUNT=1rules whose current-year occurrence is already past as suspicious instead of silently rolling them into the next year.
- Display the computed next execution time immediately in the UI and creation response.
- Add telemetry for large differences between the requested time and normalized
nextRunAt.
Related issue
During later cleanup, the automation-management handler became unavailable. Removing the automation definition stopped scheduler enumeration, but an orphaned ACTIVE row remained in SQLite. This was not the cause of the missed run, but it indicates a separate consistency and cleanup issue.
Mitigation applied
Critical review waits no longer rely on detached heartbeat automations. The workflow now uses an active, bounded wait loop with explicit polling and visible progress. This avoids timezone normalization ambiguity and ensures that the task itself remains responsible for observing the review gate.
1 Comment
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action