Skip to content

fix: handle null token_usage in optimizer jq aggregations#24400

Merged
lpcox merged 1 commit intomainfrom
fix/token-optimizer-null-safety
Apr 3, 2026
Merged

fix: handle null token_usage in optimizer jq aggregations#24400
lpcox merged 1 commit intomainfrom
fix/token-optimizer-null-safety

Conversation

@lpcox
Copy link
Copy Markdown
Collaborator

@lpcox lpcox commented Apr 3, 2026

Problem

The Copilot Token Optimizer failed in run 23965804699 with:

jq: error: null (null) and number (1) cannot be divided

The token_usage field in RunData uses omitempty, so when a run has zero token usage the field is omitted from JSON. In jq, accessing an omitted field yields null. When all runs in a workflow group have null token usage:

  • map(.token_usage) | addnull
  • null / length → division error

Fix

  • Add // 0 null coalescing: map(.token_usage // 0) | add
  • Replace hardcoded total_cost: 0 with actual field: map(.estimated_cost // 0) | add
  • Add inline schema comments in the workflow jq blocks documenting the RunData fields and their nullability (source of truth: pkg/cli/logs_report.go)

Files Changed

  • .github/workflows/copilot-token-optimizer.md — null-safe jq, schema comments, estimated_cost field
  • .github/workflows/claude-token-optimizer.md — same changes
  • Corresponding .lock.yml files (recompiled)

No changes to pkg/cli/logs_report.go — the Go struct is unchanged; comments in the workflows reference it as the schema source.

Testing

  • Both workflows compile successfully
  • Hash consistency test passes (184/184 workflows)

When runs have zero token usage, the field is omitted from JSON
(omitempty), causing jq to see null. The expression
`map(.token_usage) | add` returns null when all values are null,
and `null / length` fails with:
  null (null) and number (1) cannot be divided

Fix:
- Use `// 0` fallback: `map(.token_usage // 0) | add`
- Use actual estimated_cost field instead of hardcoded 0
- Add schema comments documenting RunData fields and nullability

Fixes failed run: github/gh-aw/actions/runs/23965804699

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 3, 2026 23:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes jq aggregation failures in the token optimizer workflows when token_usage is omitted (JSON omitempty → jq null), and improves cost aggregation.

Changes:

  • Update jq aggregations to null-coalesce token_usage and estimated_cost with // 0 to prevent division errors.
  • Compute total_cost from run data instead of hardcoding 0.
  • Regenerate the corresponding .lock.yml workflow outputs.
Show a summary per file
File Description
.github/workflows/copilot-token-optimizer.md Makes jq aggregations resilient to missing token_usage/estimated_cost fields; adds inline schema notes.
.github/workflows/copilot-token-optimizer.lock.yml Recompiled lockfile reflecting the updated workflow script content.
.github/workflows/claude-token-optimizer.md Same jq null-coalescing + cost aggregation improvements as the Copilot workflow.
.github/workflows/claude-token-optimizer.lock.yml Recompiled lockfile reflecting the updated workflow script content.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (4)

.github/workflows/copilot-token-optimizer.md:126

  • The jq filter computes the same total token sum twice (for total_tokens and again inside avg_tokens). Consider binding the sum to a local variable within the map(...) expression and reusing it; this reduces duplication and makes the aggregation easier to maintain.
        map({
          workflow: .[0].workflow_name,
          total_tokens: (map(.token_usage // 0) | add),
          total_cost: (map(.estimated_cost // 0) | add),
          run_count: length,
          avg_tokens: ((map(.token_usage // 0) | add) / length),
          run_ids: map(.database_id),

.github/workflows/copilot-token-optimizer.md:115

  • The schema comment “omitempty → null when 0” is a bit imprecise: omitempty causes the field to be omitted when zero, and jq sees a missing field as null. Consider rewording to reflect “omitted when 0 → jq yields null when missing” to avoid implying the JSON explicitly contains null.
      # Schema: gh aw logs --json → LogsData.runs[] (RunData from pkg/cli/logs_report.go)
      #   .workflow_name (string), .token_usage (int, omitempty → null when 0),
      #   .estimated_cost (float, omitempty), .database_id (int64), .created_at (time), .url (string)

.github/workflows/claude-token-optimizer.md:126

  • The jq filter computes the same total token sum twice (for total_tokens and again inside avg_tokens). Consider binding the sum to a local variable within the map(...) expression and reusing it; this reduces duplication and makes the aggregation easier to maintain.
        map({
          workflow: .[0].workflow_name,
          total_tokens: (map(.token_usage // 0) | add),
          total_cost: (map(.estimated_cost // 0) | add),
          run_count: length,
          avg_tokens: ((map(.token_usage // 0) | add) / length),
          run_ids: map(.database_id),

.github/workflows/claude-token-optimizer.md:115

  • The schema comment “omitempty → null when 0” is a bit imprecise: omitempty causes the field to be omitted when zero, and jq sees a missing field as null. Consider rewording to reflect “omitted when 0 → jq yields null when missing” to avoid implying the JSON explicitly contains null.
      # Schema: gh aw logs --json → LogsData.runs[] (RunData from pkg/cli/logs_report.go)
      #   .workflow_name (string), .token_usage (int, omitempty → null when 0),
      #   .estimated_cost (float, omitempty), .database_id (int64), .created_at (time), .url (string)
  • Files reviewed: 4/4 changed files
  • Comments generated: 2

Comment on lines 112 to +115
# Find the most expensive workflow (by total tokens across all its runs)
# Schema: gh aw logs --json → LogsData.runs[] (RunData from pkg/cli/logs_report.go)
# .workflow_name (string), .token_usage (int, omitempty → null when 0),
# .estimated_cost (float, omitempty), .database_id (int64), .created_at (time), .url (string)
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions adding schema comments in pkg/cli/logs_report.go, but this change appears to add schema comments here in the workflow instead. If the Go schema documentation was also intended to change, please include it in the PR; otherwise consider updating the PR description/files-changed list to match what’s actually modified.

This issue also appears in the following locations of the same file:

  • line 113
  • line 120

Copilot uses AI. Check for mistakes.
Comment on lines 112 to +115
# Find the most expensive workflow (by total tokens across all its runs)
# Schema: gh aw logs --json → LogsData.runs[] (RunData from pkg/cli/logs_report.go)
# .workflow_name (string), .token_usage (int, omitempty → null when 0),
# .estimated_cost (float, omitempty), .database_id (int64), .created_at (time), .url (string)
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions adding schema comments in pkg/cli/logs_report.go, but this change appears to add schema comments here in the workflow instead. If the Go schema documentation was also intended to change, please include it in the PR; otherwise consider updating the PR description/files-changed list to match what’s actually modified.

This issue also appears in the following locations of the same file:

  • line 113
  • line 120

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

@lpcox lpcox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed the review feedback — the PR description was misleading. The schema comments are intentionally inline in the workflow .md files (next to the jq expressions that consume the data), not in pkg/cli/logs_report.go. The Go struct is unchanged; the inline comments reference it as the source of truth for field names and nullability. Updated the PR description to clarify this.

@lpcox lpcox merged commit 5401131 into main Apr 3, 2026
121 checks passed
@lpcox lpcox deleted the fix/token-optimizer-null-safety branch April 3, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants