floci-io/floci · error · AwsException

ValidationError

ValidationError

Error message

Property VpcId is required for AWS::EC2::VPCGatewayAttachment

What it means

ValidationError from the AWS::EC2::VPCGatewayAttachment provisioner when VpcId resolves to null/blank. Without the guard the attachment would be recorded with a null VpcId, which leaks into DescribeInternetGateways output and makes every later DetachInternetGateway on that gateway throw.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/cloudformation/provisioners/Ec2VpcGatewayAttachmentCfnProvisioner.java:47

    private final Ec2Service ec2Service;

    @Inject
    public Ec2VpcGatewayAttachmentCfnProvisioner(Ec2Service ec2Service) {
        this.ec2Service = ec2Service;
    }

    @Override
    public Set<String> resourceTypes() {
        return Set.of("AWS::EC2::VPCGatewayAttachment");
    }

    @Override
    public void provision(StackResource r, JsonNode props, ProvisionContext ctx) {
        String vpcId = ctx.resolveOptional(props, "VpcId");
        if (vpcId == null || vpcId.isBlank()) {
            // Without this the attachment lands with a null VpcId, which both shows up in
            // DescribeInternetGateways and makes every later detach on that gateway throw.
            throw new AwsException("ValidationError",
                    "Property VpcId is required for AWS::EC2::VPCGatewayAttachment", 400);
        }

        String igwId = ctx.resolveOptional(props, "InternetGatewayId");
        String vgwId = ctx.resolveOptional(props, "VpnGatewayId");
        boolean hasIgw = igwId != null && !igwId.isBlank();
        boolean hasVgw = vgwId != null && !vgwId.isBlank();
        if (hasIgw == hasVgw) {
            throw new AwsException("ValidationError",
                    "AWS::EC2::VPCGatewayAttachment requires exactly one of "
                    + "InternetGatewayId or VpnGatewayId", 400);
        }
        if (hasVgw) {
            // There is no VPN gateway model to attach to and nothing DescribeVpnGateways
            // could report afterwards, so recording a physical id would report
            // CREATE_COMPLETE for an attachment that does not exist. Fail instead.
            throw new AwsException("ValidationError",
                    "AWS::EC2::VPCGatewayAttachment with VpnGatewayId is not supported: "

View on GitHub (pinned to 62ff490619)

Solutions

  1. Add an explicit VpcId to the resource: VpcId: !Ref Vpc (or the actual vpc-... id).
  2. If it references a parameter, ensure the deploy passes it: --parameters ParameterKey=VpcId,ParameterValue=vpc-....
  3. If it uses Fn::ImportValue, verify the export exists (ListExports) before deploying.

Example fix

# before
Resources:
  Attach:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref Igw   # VpcId missing

# after
Resources:
  Attach:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref Vpc
      InternetGatewayId: !Ref Igw
Defensive patterns

Strategy: validation

Validate before calling

// Client-side template check before deploy
Object attach = templateAt("Resources/AttachGw/Properties");
if (attach == null || isBlank(((Map<?,?>) attach).get("VpcId"))) {
    throw new IllegalArgumentException("VPCGatewayAttachment requires VpcId");
}

Try / catch

catch AwsException/ValidationError from CreateStack; the stack event for the attachment resource carries the reason — fix the template, do not retry.

Prevention

When it happens

Trigger: A template declares AWS::EC2::VPCGatewayAttachment without a VpcId property, or with a VpcId that resolves to empty — e.g. !Ref on a parameter that was not passed, or !Sub on an unresolved import.

Common situations: Templates copied from AWS samples where VpcId comes from a parameter with no default and the emulator deploy omitted it; Fn::ImportValue of an export that does not exist (which resolves empty here rather than failing at resolution).

Understand the failure class

Background: ValidationError explained: why open-source libraries reject your input — file uploads, YAML manifests, unique fields, and query permissions — this error's family across 13 libraries.

Related errors


AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14). Data as JSON: /api/errors/69cebfecfa9ddc1d. Report an issue: GitHub.