I have tried to verify manually that this is indeed an issue on my own data, but reproduction requires an instance with runs.
Summary
gh aw logs appears to return workflow runs whose created_at values fall outside the
requested --start-date / --end-date window.
The core issue appears to be that gh aw logs translates a bounded date range into multiple
gh run list --created ... flags instead of a single --created "start..end" range.
That split form does not reliably enforce both bounds. Pagination then adds a third
--created flag and can worsen the problem, but the two-boundary case already appears broken.
Expected behavior
When running:
gh aw logs <workflow> --start-date <start> --end-date <end> --count <n> --json
every returned run should satisfy:
created_at >= start
created_at <= end
and the implementation should preserve that bounded range exactly.
Actual behavior
gh aw logs can return runs whose created_at values are earlier than the requested
--start-date or otherwise outside the intended range.
This makes date-bounded queries unreliable for exact analysis.
Evidence
Where gh aw logs constructs the split form
In pkg/cli/logs_github_api.go, listWorkflowRunsWithPagination builds the gh run list
command arguments like this:
if opts.StartDate != "" {
args = append(args, "--created", ">="+opts.StartDate)
}
if opts.EndDate != "" {
args = append(args, "--created", "<="+opts.EndDate)
}
if opts.BeforeDate != "" {
args = append(args, "--created", "<"+opts.BeforeDate)
}
In pkg/cli/logs_orchestrator.go, DownloadWorkflowLogs passes StartDate, EndDate, and
BeforeDate into that function:
runs, totalFetched, err := listWorkflowRunsWithPagination(ListWorkflowRunsOptions{
WorkflowName: workflowName,
Limit: batchSize,
StartDate: startDate,
EndDate: endDate,
BeforeDate: beforeDate,
...
})
and the pagination cursor is derived from the oldest run in the previous batch:
oldestRun := runs[len(runs)-1]
beforeDate = oldestRun.CreatedAt.Format(time.RFC3339)
So gh aw logs is concretely constructing bounded date queries as repeated --created
arguments:
gh run list ... \
--created ">=START" \
--created "<=END"
and with pagination:
gh run list ... \
--created ">=START" \
--created "<=END" \
--created "<BEFORE_DATE"
Why that shape is unsafe
In GitHub CLI, gh run list defines --created as a single string flag, not a multi-value
slice:
type ListOptions struct {
...
Created string
...
}
cmd.Flags().StringVarP(&opts.Created, "created", "", "", "Filter runs by the `date` it was created")
and later passes one created filter value through:
filters := &shared.FilterOptions{
...
Created: opts.Created,
...
}
Minimal reproduction
The reproduction below uses only public CLI behavior.
1. Choose a repo and workflow with runs around a UTC date boundary
You need a workflow with runs near the beginning or end of a UTC day.
Set placeholders:
REPO=OWNER/REPO
WORKFLOW=workflow-name
DAY=2026-04-17
2. Verify the strict expected window with gh run list using range syntax
gh run list -R "$REPO" --workflow "$WORKFLOW" \
--limit 250 \
--json databaseId,createdAt \
--created "$DAY..${DAY}T23:59:59Z"
Check that all returned createdAt values are within the UTC day.
3. Show that the split --created form is unsafe
This mirrors the way gh aw logs constructs a bounded range:
gh run list -R "$REPO" --workflow "$WORKFLOW" \
--limit 250 \
--json databaseId,createdAt \
--created ">=$DAY" \
--created "<=$DAY"
If the bug reproduces, this split form returns runs outside the intended day while the single
range form stays correctly bounded.
4. Run the equivalent gh aw logs query
gh aw logs "$WORKFLOW" \
--repo "$REPO" \
--start-date "$DAY" \
--end-date "$DAY" \
--count 250 \
--json > aw.json
5. Inspect the returned date bounds
jq '{
runs: (.runs | length),
min_created_at: ([.runs[].created_at] | min),
max_created_at: ([.runs[].created_at] | max)
}' aw.json
6. Observe the discrepancy
If the bug reproduces, min_created_at from gh aw logs will be earlier than the requested
day even though the strict gh run list --created "start..end" query stays inside the day.
Minimal local proof
A small direct probe against gh run list shows the difference clearly.
Single range expression:
gh run list -R "$REPO" --workflow "$WORKFLOW" \
--limit 1000 \
--json databaseId,createdAt \
--created '2026-04-17T23:20:00Z..2026-04-17T23:45:00Z'
This returned only runs inside the interval.
Equivalent split form:
gh run list -R "$REPO" --workflow "$WORKFLOW" \
--limit 1000 \
--json databaseId,createdAt \
--created '>=2026-04-17T23:20:00Z' \
--created '<=2026-04-17T23:45:00Z'
This returned many runs from much earlier timestamps, showing that the lower bound was not
enforced.
An exact-point test showed the same issue:
gh run list ... --created '2026-04-17T23:30:42Z..2026-04-17T23:30:42Z'
correctly returned zero results, while:
gh run list ... \
--created '>=2026-04-17T23:30:42Z' \
--created '<=2026-04-17T23:30:42Z'
returned many earlier runs, again showing that the split form does not enforce both bounds.
Why this matters
- Date-bounded log analysis becomes inaccurate.
- Exact per-day or per-window token/cost analysis can be polluted by out-of-window runs.
- Increasing
--count can change which out-of-window runs appear, making the behavior hard to
reason about.
Proposed fix
Use a single --created range expression instead of appending multiple --created flags.
For example:
- first page:
--created "START..END"
- next page:
--created "START..BEFORE_DATE"
or equivalent logic that maintains one bounded range per request.
In other words, gh aw logs should represent the date window as one --created value, and if
pagination is needed it should update the upper bound inside that same range expression instead
of adding another --created flag.
Relevant code paths
pkg/cli/logs_github_api.go
pkg/cli/logs_orchestrator.go
Notes
This report is about the run-discovery path used by gh aw logs --start-date/--end-date.
It does not affect workflows that bypass date discovery by supplying explicit run IDs.
I have tried to verify manually that this is indeed an issue on my own data, but reproduction requires an instance with runs.
Summary
gh aw logsappears to return workflow runs whosecreated_atvalues fall outside therequested
--start-date/--end-datewindow.The core issue appears to be that
gh aw logstranslates a bounded date range into multiplegh run list --created ...flags instead of a single--created "start..end"range.That split form does not reliably enforce both bounds. Pagination then adds a third
--createdflag and can worsen the problem, but the two-boundary case already appears broken.Expected behavior
When running:
every returned run should satisfy:
created_at >= startcreated_at <= endand the implementation should preserve that bounded range exactly.
Actual behavior
gh aw logscan return runs whosecreated_atvalues are earlier than the requested--start-dateor otherwise outside the intended range.This makes date-bounded queries unreliable for exact analysis.
Evidence
Where
gh aw logsconstructs the split formIn
pkg/cli/logs_github_api.go,listWorkflowRunsWithPaginationbuilds thegh run listcommand arguments like this:
In
pkg/cli/logs_orchestrator.go,DownloadWorkflowLogspassesStartDate,EndDate, andBeforeDateinto that function:and the pagination cursor is derived from the oldest run in the previous batch:
So
gh aw logsis concretely constructing bounded date queries as repeated--createdarguments:
and with pagination:
Why that shape is unsafe
In GitHub CLI,
gh run listdefines--createdas a single string flag, not a multi-valueslice:
and later passes one created filter value through:
Minimal reproduction
The reproduction below uses only public CLI behavior.
1. Choose a repo and workflow with runs around a UTC date boundary
You need a workflow with runs near the beginning or end of a UTC day.
Set placeholders:
2. Verify the strict expected window with
gh run listusing range syntaxCheck that all returned
createdAtvalues are within the UTC day.3. Show that the split
--createdform is unsafeThis mirrors the way
gh aw logsconstructs a bounded range:If the bug reproduces, this split form returns runs outside the intended day while the single
range form stays correctly bounded.
4. Run the equivalent
gh aw logsquery5. Inspect the returned date bounds
6. Observe the discrepancy
If the bug reproduces,
min_created_atfromgh aw logswill be earlier than the requestedday even though the strict
gh run list --created "start..end"query stays inside the day.Minimal local proof
A small direct probe against
gh run listshows the difference clearly.Single range expression:
This returned only runs inside the interval.
Equivalent split form:
This returned many runs from much earlier timestamps, showing that the lower bound was not
enforced.
An exact-point test showed the same issue:
gh run list ... --created '2026-04-17T23:30:42Z..2026-04-17T23:30:42Z'correctly returned zero results, while:
returned many earlier runs, again showing that the split form does not enforce both bounds.
Why this matters
--countcan change which out-of-window runs appear, making the behavior hard toreason about.
Proposed fix
Use a single
--createdrange expression instead of appending multiple--createdflags.For example:
--created "START..END"--created "START..BEFORE_DATE"or equivalent logic that maintains one bounded range per request.
In other words,
gh aw logsshould represent the date window as one--createdvalue, and ifpagination is needed it should update the upper bound inside that same range expression instead
of adding another
--createdflag.Relevant code paths
pkg/cli/logs_github_api.gopkg/cli/logs_orchestrator.goNotes
This report is about the run-discovery path used by
gh aw logs --start-date/--end-date.It does not affect workflows that bypass date discovery by supplying explicit run IDs.