{"record":{"id":"0321cf02f2d1bbc8","repo":"floci-io/floci","slug":"validationexception-0321cf","errorCode":"ValidationException","errorMessage":"Dimensions filter requires a Key.","messagePattern":"Dimensions filter requires a Key\\.","errorType":"error_code","errorClass":"AwsException","httpStatus":400,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/services/ce/FilterExpressionEvaluator.java","lineNumber":71,"sourceCode":"        if (dimensions.isObject() && !dimensions.isEmpty()) {\n            return matchDimensions(dimensions, line);\n        }\n        JsonNode tags = expression.path(\"Tags\");\n        if (tags.isObject() && !tags.isEmpty()) {\n            return matchTags(tags, line);\n        }\n        JsonNode costCategories = expression.path(\"CostCategories\");\n        if (costCategories.isObject() && !costCategories.isEmpty()) {\n            // No cost categories defined — never matches.\n            return false;\n        }\n        return true;\n    }\n\n    private static boolean matchDimensions(JsonNode node, UsageLine line) {\n        String key = node.path(\"Key\").asText(null);\n        if (key == null || key.isEmpty()) {\n            throw new AwsException(\"ValidationException\",\n                    \"Dimensions filter requires a Key.\", 400);\n        }\n        Set<String> values = readValues(node.path(\"Values\"));\n        Set<String> matchOptions = readValues(node.path(\"MatchOptions\"));\n        boolean caseInsensitive = matchOptions.contains(\"CASE_INSENSITIVE\");\n        String actual = dimensionValue(key, line);\n        return valueMatches(actual, values, caseInsensitive);\n    }\n\n    private static boolean matchTags(JsonNode node, UsageLine line) {\n        String key = node.path(\"Key\").asText(null);\n        if (key == null || key.isEmpty()) {\n            throw new AwsException(\"ValidationException\",\n                    \"Tags filter requires a Key.\", 400);\n        }\n        Set<String> values = readValues(node.path(\"Values\"));\n        Set<String> matchOptions = readValues(node.path(\"MatchOptions\"));\n        boolean caseInsensitive = matchOptions.contains(\"CASE_INSENSITIVE\");","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/services/ce/FilterExpressionEvaluator.java#L53-L89","documentation":"A Cost Explorer Filter of type Dimensions must carry a Key naming which dimension to match (SERVICE, REGION, ...). Floci evaluates filters while scanning usage lines and throws as soon as it hits a Dimensions node without a Key.","triggerScenarios":"GetCostAndUsage / GetDimensionValues / GetCostAndUsageWithResources with Filter: {Dimensions: {Values: [\"Amazon EC2\"]}} — Values present but Key missing or empty.","commonSituations":"Hand-built filter JSON copied from an example that omitted Key; dynamically assembled filters where the key field is optional in the UI model but required by the API.","solutions":["Add Key to the Dimensions filter: {\"Dimensions\": {\"Key\": \"SERVICE\", \"Values\": [\"Amazon EC2\"]}}","Use the SDK's builder (DimensionValues.builder().key(Dimension.SERVICE)) which makes the required shape obvious","Validate filter JSON against the CE model before sending"],"exampleFix":"// before\nFilter.builder().dimensions(d -> d.values(\"Amazon EC2\")).build();\n// after\nFilter.builder().dimensions(d -> d.key(Dimension.SERVICE).values(\"Amazon EC2\")).build();","handlingStrategy":"type-guard","validationCode":"if (filter.dimensions() != null && (filter.dimensions().key() == null || filter.dimensions().key().isBlank())) {\n    throw new IllegalArgumentException(\"Dimensions filter requires Key\");\n}","typeGuard":"function isDimensionFilter(f: unknown): f is { Dimensions: { Key: string; Values: string[] } } {\n  const d = (f as any)?.Dimensions;\n  return !!d && typeof d.Key === \"string\" && d.Key.length > 0 && Array.isArray(d.Values);\n}","tryCatchPattern":null,"preventionTips":["Use the AWS SDK builder types — they surface required members at compile time","Validate filter JSON with a schema before sending to CE"],"tags":["cost-explorer","filter","validation","floci"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}