TL;DR: Put the earlier date in Start and the later date in End, format both as YYYY-MM-DD, and remember the end date is exclusive, so a one-day query needs End set to the day after Start. Most occurrences are a swapped pair or a same-day range; fix the order and the off-by-one and the call succeeds.

The exact error:

```text
InvalidTimeRangeException: End date must be after start date
```

1. Print the actual Start and End values your code is sending, not the ones you think it is sending.
   Expected: you see the swap, the same-day pair, or the malformed string immediately.
2. Fix the order so Start is the earlier date and End the later date, both in YYYY-MM-DD format with no time component.
   Expected: the pair reads chronologically when you look at it.
3. Account for the exclusive end: to query a single day, set End to the day after Start. To query September, use Start 2026-09-01 and End 2026-10-01.
   Expected: the range covers exactly the days you intend, with no off-by-one.
4. Keep both dates out of the future and Start within the last twelve months, or you will trade this exception for an empty result.
   Expected: dates inside the window Cost Explorer actually retains.
5. Re-run the call: `aws ce get-cost-and-usage --time-period Start=2026-09-01,End=2026-10-01 --granularity MONTHLY --metrics BlendedCost`
   Expected: a 200 response with cost rows instead of the exception.

## Use this when
- GetCostAndUsage throws InvalidTimeRangeException
- A date-range builder in an agent or script constructs Start and End programmatically
- A single-day query returns the exception instead of data
- Timezone conversions shift a date across midnight and invert the pair

## Not for this skill when
- The call succeeds but returns no data, which is the data-availability issue, not a malformed range
- The error is AccessDeniedException, which is permissions
- Dates are fine but the granularity is rejected, which is a different validation error

## Variant phrasings
- InvalidTimeRangeException GetCostAndUsage
- end date before start date cost explorer API
- cost explorer API date format
- exclusive end date GetCostAndUsage

## Why it happens
Programmatic date builders love off-by-one mistakes: Start set to today and End set to today looks like a one-day query to a human but is an empty range to an API with an exclusive end. Timezone math makes it worse by shifting one side of the pair across midnight. The API validates strictly and reports the violation instead of guessing intent.

## Edge cases
- Some SDKs accept datetime objects and serialize them with time components or offsets that the API rejects; pass plain date strings
- A loop that pages month by month must advance both Start and End together or the pair inverts on the last iteration
- Daylight-saving transitions can produce a Start that equals End in local time while differing in UTC, so build ranges in UTC
- The twelve-month retention limit is enforced separately from range validity, so a valid but ancient range fails differently

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_UpUhqkpmNDzYGTqeBlyIfw
