fix(trial): record exception timestamps in UTC, not naive local time

Merged
MI
3mo ago
+2-2

Why

ExceptionInfo.from_exception records occurred_at=datetime.now() — naive, host-local. Every other timestamp written into the same result.json comes from Trial._now() (src/harbor/trial/trial.py:144), which is datetime.now(timezone.utc).

So one file carries two conventions. Reproduced on a UTC+8 host with TimingInfo(started_at=Trial._now()) and an ExceptionInfo created one millisecond later:

agent_execution : 2026-07-30T04:28:40.471429Z
exception time  : 2026-07-30T12:28:40.472075

timing tzinfo   : UTC
exception tzinfo: None
subtraction     : TypeError: can't subtract offset-naive and offset-aware datetimes

The exception looks like it happened 8 hours after the phase that raised it, and any code that subtracts the two raises TypeError. A consumer reading result.json cannot recover the intended instant, because no offset was written and the producing host's timezone is not recorded anywhere in the file.

What

datetime.now() → datetime.now(timezone.utc) in ExceptionInfo.from_exception.

After:

agent_execution : 2026-07-30T04:28:40.633146Z
exception time  : 2026-07-30T04:28:40.633454Z
subtraction     : 0:00:00.000308

Validation

  • uv run ruff check --fix . passes.
  • uv run ruff format . leaves all files unchanged.
  • uv run ty check passes.
  • occurred_at has no readers inside src/harbor, so nothing in-tree compares it against a naive value.

Sibling instances, deliberately not in this PR

grep -rn "datetime\.now()" src/harbor finds 13 more. Most are local-time strings used to name directories and tags (cli/exec.py, cli/config_init.py, cli/admin/admin.py, cli/hub_leaderboards.py, models/job/config.py), where local time is a reasonable choice and no serialized timestamp is involved.

Three are the same class as this one and worth a look, but I have not built the evidence for them so I left them alone: job.py:658 (updated_at), job.py:1085/job.py:1099 (JobResult.finished_at), and upload/uploader.py:300 (finished_at fallback). If you'd like them in the same change, say so and I'll extend the PR with per-site reproductions.


Open in Devin Review