{"record":{"id":"5be720db16d69e08","repo":"floci-io/floci","slug":"validationexception-5be720","errorCode":"ValidationException","errorMessage":"Min and max capacity are required when registering a new scalable target.","messagePattern":"Min and max capacity are required when registering a new scalable target\\.","errorType":"validation","errorClass":"AwsException","httpStatus":400,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ApplicationAutoScalingService.java","lineNumber":120,"sourceCode":"        this.regionResolver = regionResolver;\n        this.cloudWatchMetricsService = cloudWatchMetricsService;\n    }\n\n    // ---------------------------------------------------------------- scalable targets\n\n    public ScalableTarget registerScalableTarget(String serviceNamespace, String resourceId,\n                                                 String scalableDimension, Integer minCapacity,\n                                                 Integer maxCapacity, String roleArn,\n                                                 SuspendedState suspendedState,\n                                                 Map<String, String> tags, String region) {\n        validateTriple(serviceNamespace, resourceId, scalableDimension);\n\n        String key = targetKey(region, serviceNamespace, resourceId, scalableDimension);\n        ScalableTarget existing = targets.get(key).orElse(null);\n\n        if (existing == null) {\n            if (minCapacity == null || maxCapacity == null) {\n                throw new AwsException(\"ValidationException\",\n                        \"Min and max capacity are required when registering a new scalable target.\", 400);\n            }\n            ScalableTarget target = new ScalableTarget();\n            target.setServiceNamespace(serviceNamespace);\n            target.setResourceId(resourceId);\n            target.setScalableDimension(scalableDimension);\n            target.setMinCapacity(minCapacity);\n            target.setMaxCapacity(maxCapacity);\n            target.setRoleArn(roleArn != null ? roleArn : serviceLinkedRoleArn(serviceNamespace));\n            target.setScalableTargetArn(buildScalableTargetArn(region));\n            target.setCreationTime(nowEpochSeconds());\n            target.setSuspendedState(suspendedState != null ? suspendedState : new SuspendedState());\n            if (tags != null) {\n                target.setTags(tags);\n            }\n            targets.put(key, target);\n            LOG.infov(\"RegisterScalableTarget: {0} {1} {2} in {3}\",\n                    serviceNamespace, resourceId, scalableDimension, region);","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/services/applicationautoscaling/ApplicationAutoScalingService.java#L102-L138","documentation":"Thrown by ApplicationAutoScalingService.registerScalableTarget() when no existing target exists for the (region, namespace, resourceId, dimension) key and the request does not supply both MinCapacity and MaxCapacity. New registrations need the capacity bounds (AWS requires them on create), while updates of an existing target may omit them and keep prior values. HTTP 400 ValidationException, raised only on the create branch.","triggerScenarios":"Calling RegisterScalableTarget for a brand-new target with MinCapacity or MaxCapacity (or both) null — e.g. an SDK call whose builder only sets one of them, or none because the intent was 'just suspend scaling'. Re-running an update-style call after the target was deleted (now takes the create path and fails).","commonSituations":"Idempotent deployment scripts that issue the same register call with only SuspendedState on later runs; they work while the target exists but fail after a DeregisterScalableTarget or state reset. Terraform plans that set min but leave max as null. Test teardown between runs removing targets so 'update' calls become 'create' calls.","solutions":["Always send both MinCapacity and MaxCapacity (0/0 is valid if you want no capacity) on any call that may create the target.","If you intended an update, first confirm the target exists (DescribeScalableTargets); if it was deregistered, re-register with full parameters.","In IaC, make min_capacity and max_capacity required attributes of the scalable target resource so omissions fail at plan time.","For pure suspension, still include the current capacities — there is no capacity-free register in AWS semantics."],"exampleFix":"// before\nRegisterScalableTargetRequest.builder()\n    .serviceNamespace(\"dynamodb\")\n    .resourceId(\"table/mytable\")\n    .scalableDimension(\"dynamodb:table:ReadCapacityUnits\")\n    .suspendedState(susp)\n    .build();\n\n// after\nRegisterScalableTargetRequest.builder()\n    .serviceNamespace(\"dynamodb\")\n    .resourceId(\"table/mytable\")\n    .scalableDimension(\"dynamodb:table:ReadCapacityUnits\")\n    .minCapacity(5)\n    .maxCapacity(100)\n    .suspendedState(susp)\n    .build();","handlingStrategy":"validation","validationCode":"// Always carry both bounds when a target may be created\nInteger min = request.minCapacity() != null ? request.minCapacity() : 0;\nInteger max = request.maxCapacity() != null ? request.maxCapacity() : min;\nclient.registerScalableTarget(RegisterScalableTargetRequest.builder()\n    .serviceNamespace(ns).resourceId(rid).scalableDimension(dim)\n    .minCapacity(min).maxCapacity(max)\n    .build());","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Send both capacities on every register call; updates tolerate them and creates require them.","Make min/max required fields in your IaC module for scalable targets.","If your script only suspends scaling, first describe the target to reuse its stored capacities."],"tags":["application-autoscaling","scalable-target","validation"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}