Skip to content

[bug-hunter] Timezone-aware --since filter drops valid workflow runs in fetch-workflow-logs.py #383

Description

@github-actions

Impact

Users running scripts/fetch-workflow-logs.py with a timezone-aware ISO 8601 --since value can get no runs even when matching runs exist, so downstream log triage misses failures.

Reproduction Steps

  1. Create this repro script:
# /tmp/gh-aw/agent/repro_fetch_logs_since_tz.py
import json
import importlib.util
from pathlib import Path

module_path = Path('/home/runner/work/ai-github-actions/ai-github-actions/scripts/fetch-workflow-logs.py')
spec = importlib.util.spec_from_file_location('fetch_workflow_logs', module_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)


def fake_github_api(path: str, token: str, accept: str = "application/vnd.github+json") -> bytes:
    if "page=1" in path:
        payload = {
            "workflow_runs": [
                {"id": 1, "created_at": "2026-02-01T09:30:00Z", "conclusion": "failure"},
                {"id": 2, "created_at": "2026-02-01T08:30:00Z", "conclusion": "failure"},
            ]
        }
    else:
        payload = {"workflow_runs": []}
    return json.dumps(payload).encode()

mod.github_api = fake_github_api
runs = mod.list_workflow_runs(
    repo="o/r",
    workflow="wf.yml",
    token="t",
    since="2026-02-01T10:00:00+01:00",  # equals 09:00:00Z
    until=None,
    conclusion="failure",
    last=10,
)

actual = [r["id"] for r in runs]
print("returned_run_ids", actual)
expected = [1]
if actual != expected:
    raise SystemExit(f"BUG: expected {expected}, got {actual}")
  1. Run:
python /tmp/gh-aw/agent/repro_fetch_logs_since_tz.py

Expected vs Actual

Expected: run id=1 is included because 09:30:00Z is after 09:00:00Z (2026-02-01T10:00:00+01:00).

Actual: no runs are returned.

Actual output:

returned_run_ids []
BUG: expected [1], got []

Failing Test

import json
import importlib.util
from pathlib import Path

module_path = Path('/home/runner/work/ai-github-actions/ai-github-actions/scripts/fetch-workflow-logs.py')
spec = importlib.util.spec_from_file_location('fetch_workflow_logs', module_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)


def fake_github_api(path: str, token: str, accept: str = "application/vnd.github+json") -> bytes:
    if "page=1" in path:
        payload = {
            "workflow_runs": [
                {"id": 1, "created_at": "2026-02-01T09:30:00Z", "conclusion": "failure"},
                {"id": 2, "created_at": "2026-02-01T08:30:00Z", "conclusion": "failure"},
            ]
        }
    else:
        payload = {"workflow_runs": []}
    return json.dumps(payload).encode()

mod.github_api = fake_github_api
runs = mod.list_workflow_runs(
    repo="o/r",
    workflow="wf.yml",
    token="t",
    since="2026-02-01T10:00:00+01:00",
    until=None,
    conclusion="failure",
    last=10,
)

actual = [r["id"] for r in runs]
assert actual == [1], actual

Evidence

  • scripts/fetch-workflow-logs.py:69 compares timestamps as raw strings (if since and created < since:), which breaks for valid ISO 8601 formats with timezone offsets.
  • Reproduction output above shows deterministic failure (expected [1], got []).

What is this? | From workflow: Bug Hunter

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

  • expires on Mar 2, 2026, 11:27 AM UTC

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions