floci-io/floci · error · AwsException
ValidationException
ValidationException
Error message
1 validation error detected: Value at 'Metrics' failed to satisfy constraint: Member must contain at least 1 element.
What it means
Cost Explorer GetCostAndUsage/GetCostAndUsageWithResources requires at least one metric (e.g. UnblendedCost, UsageQuantity). Floci replicates AWS's exact validation message: 'Metrics' must contain at least 1 element.
Source
Thrown at src/main/java/io/github/hectorvent/floci/services/ce/CostExplorerService.java:87
/**
* Returns the same shape as {@link #getCostAndUsage} for now. Floci's
* resource-level data is already surfaced through the {@code RESOURCE_ID}
* dimension, so a caller that wants resource breakdown can issue
* {@code GetCostAndUsage} with {@code GroupBy=[{Type:DIMENSION,Key:RESOURCE_ID}]}.
* A separate emit path that returns inline resource attributions can land
* later if a consumer needs it.
*/
public ObjectNode getCostAndUsageWithResources(JsonNode request, String defaultRegion) {
return runCostAndUsage(request, defaultRegion);
}
private ObjectNode runCostAndUsage(JsonNode request, String defaultRegion) {
TimeWindow window = parseTimeWindow(request);
TimeBucketing.Granularity granularity = TimeBucketing.parseGranularity(
request.path("Granularity").asText(null));
Set<String> metrics = GroupAggregator.parseMetrics(request.path("Metrics"));
if (metrics.isEmpty()) {
throw new AwsException("ValidationException",
"1 validation error detected: Value at 'Metrics' failed to satisfy constraint: Member must contain at least 1 element.", 400);
}
List<GroupAggregator.GroupBy> groupBys = GroupAggregator.parseGroupBy(request.path("GroupBy"));
JsonNode filter = request.has("Filter") ? request.get("Filter") : null;
List<UsageLine> all = collectLines(window.start(), window.end(), defaultRegion);
// Apply filter once across the full window so the same set is reused
// per bucket (lines are emitted per request scope, no cross-bucket leakage).
List<UsageLine> filtered = new ArrayList<>();
for (UsageLine line : all) {
if (FilterExpressionEvaluator.matches(filter, line)) {
filtered.add(line);
}
}
CostSynthesizer synthesizer = new CostSynthesizer(rateLookup);
if (monthlyCreditUsd > 0) {
CreditLineEmitter creditEmitter = new CreditLineEmitter(monthlyCreditUsd);View on GitHub (pinned to 62ff490619)
Solutions
- Add at least one metric: Metrics: ["UnblendedCost"] (and typically also "UsageQuantity")
- Default the metrics list client-side when user selection is empty
- Verify with the AWS CLI first: aws ce get-cost-and-usage --metrics UnblendedCost ...
Example fix
// before
GetCostAndUsageRequest.builder().timePeriod(tp).granularity(DAILY).build();
// after
GetCostAndUsageRequest.builder().timePeriod(tp).granularity(DAILY)
.metrics(Metric.UNBLENDED_COST, Metric.USAGE_QUANTITY).build(); Defensive patterns
Strategy: validation
Validate before calling
if (metrics == null || metrics.isEmpty()) metrics = List.of(Metric.UNBLENDED_COST);
Prevention
- Default to UnblendedCost client-side when the caller passes no metrics
- Copy the request shape from aws ce get-cost-and-usage examples
When it happens
Trigger: Calling getCostAndUsage with Metrics absent, an empty array, or a non-array (null/empty string) — GroupAggregator.parseMetrics returns an empty set and the guard fires.
Common situations: Building the request conditionally so the metrics list ends up empty; porting scripts that relied on a server-side default metric.
Related errors
- ValidationException
- ValidationException
- ValidationException
- ValidationException
- TLS enabled but no certificate provided and self-signed gene
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/07a216843eb85633.
Report an issue: GitHub.