microsoft / microsoft/finops-toolkit

monthly-cost-change-percentage returns inf when the prior month is zero

Open Beginner friendly
#2,323 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Micro PR 🔬 Skill: KQL Status: 🔬 Code review Tool: FinOps hubs Type: Bug 🐛
Dominant language
PowerShell
Stars
603
Forks
248
Avg merge
7d 11h
Merged PRs (30d)
11

Description

🐛 Problem

src/queries/catalog/monthly-cost-change-percentage.kql guards its division with isempty(PreviousBilledCost).

isempty() is true for the null that prev() returns on the first row, but it is false for 0.0. A month whose prior month summed to exactly zero therefore falls through the guard, and KQL evaluates x / 0.0 as inf rather than raising an error — so an infinity is returned to the caller as a percentage.

| project ChargePeriodStart,
    BilledCostChangePct = iff(isempty(PreviousBilledCost), 0.0, toreal((BilledCost - PreviousBilledCost) * 100.0 / PreviousBilledCost)),
    EffectiveCostChangePct = iff(isempty(PreviousEffectiveCost), 0.0, toreal((EffectiveCost - PreviousEffectiveCost) * 100.0 / PreviousEffectiveCost))

👣 Repro steps

No hub required — the shape of the pipeline is enough:

datatable(ChargePeriodStart:datetime, BilledCost:real, EffectiveCost:real)
[
    datetime(2025-01-01), 100.0, 100.0,
    datetime(2025-02-01),   0.0,  80.0,
    datetime(2025-03-01),  50.0, 120.0,
]
| order by ChargePeriodStart asc
| extend PreviousBilledCost = prev(BilledCost)
| project ChargePeriodStart,
    BilledCostChangePct = iff(isempty(PreviousBilledCost), 0.0, toreal((BilledCost - PreviousBilledCost) * 100.0 / PreviousBilledCost))
ChargePeriodStart BilledCostChangePct
2025-01-01 0
2025-02-01 -100
2025-03-01 inf
Is a zero month reachable?

Yes, and BilledCost is the likelier of the two to hit it. On usage rows covered by a commitment discount BilledCost is zero — the charge was billed at purchase time, and the amortized amount lands in EffectiveCost instead. Any scope whose month is entirely commitment-covered therefore sums BilledCost to exactly zero, and the following month's BilledCostChangePct comes back as inf.

Scoping the query with an extra | where is a normal way to use it, which makes this easier to reach than the estate-wide default suggests.

🤔 Expected

The same value the guard already returns for the undefined case — 0.0 — rather than an infinity.

The == 0.0 form is already the convention elsewhere in the catalog, in service-price-benchmarking.kql:

x_CommitmentDiscountPercent = iif(ContractedCost == 0.0, 0.0, toreal(ContractedCost - EffectiveCost) * 100.0 / toreal(ContractedCost)),

🔧 Environment

  • Affected file: src/queries/catalog/monthly-cost-change-percentage.kql on dev
  • FinOps hub version: any — reproducible standalone with datatable, no ingestion involved
  • Billing account type: any
  • Cost Management export: any

ℹ️ Additional context

A separate, arguable point that I have deliberately left out of the fix: returning 0.0 when there is no prior month conflates "no change" with "no data to compare against". real(null) would distinguish them, but that is a behaviour change for existing consumers, so the PR keeps the current convention and only closes the divide-by-zero hole. Happy to follow up if you would prefer the stricter version.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/queries/catalog/monthly-cost-change-percentage.kql and inspect the guards around PreviousBilledCost and PreviousEffectiveCost. Reproduce the zero-prior-month case with the standalone datatable example from the issue. Done means both zero-prior-cost cases return 0.0 instead of inf while the first-row behavior remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure
Domain
analytics, data
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
90/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.