Describe the bug
An invalid or missing CIDR mask is never validated, so it flows into the address arithmetic in CidrBlock and surfaces as an error naming an IP address the caller never wrote — or, for a mask above 32, produces a nonsensical block with no error at all.
Forgetting the mask is an easy mistake, and the resulting message gives no hint that the mask is the problem:
ec2.IpAddresses.cidr('10.0.0.0')
-> Error: NaN.NaN.NaN.NaN is not a valid IP Address
Nothing in that message points at the missing /16.
Full behaviour of IpAddresses.cidr() on aws-cdk-lib 2.263.0:
| input |
result |
'10.0.0.0' (no mask) |
❌ NaN.NaN.NaN.NaN is not a valid IP Address |
'10.0.0.0/' |
❌ NaN.NaN.NaN.NaN is not a valid IP Address |
'10.0.0.0/abc' |
❌ NaN.NaN.NaN.NaN is not a valid IP Address |
'10.0.0.0/-1' |
❌ 512.0.0.0 is not a valid IP Address |
'10.0.0.0/33' |
⚠️ no error — silently becomes 9.255.255.255/33 |
The /33 row is the worst of these: new CidrBlock('10.0.0.0/33') returns a block whose cidr is 9.255.255.255/33, an address outside the range the caller asked for, and synthesis continues. The failure only appears later — either as a confusing downstream error (1 of /24 exceeds remaining space of 9.255.255.255/33) or at deploy time from CloudFormation.
Expected Behavior
An invalid mask should fail fast with an error that names the mask as the problem and states the accepted range, per the repo's error-message guidance (include the wrong value, the expected values, and what to change). Something like:
invalid CIDR mask in "10.0.0.0", expected an integer between 0 and 32 after a '/', e.g. '10.0.0.0/16'
Current Behavior
The mask is read with parseInt and used without any validation:
// packages/aws-cdk-lib/aws-ec2/lib/network-util.ts
this.mask = parseInt(ipAddressOrCidr.split('/')[1], 10);
this.networkAddress = NetworkUtils.ipToNum(ipAddressOrCidr.split('/')[0]) +
CidrBlock.calculateNetsize(this.mask) - 1;
With no mask, parseInt(undefined, 10) is NaN, so calculateNetsize returns 2 ** (32 - NaN) = NaN, networkAddress becomes NaN, and numToIp finally reports NaN.NaN.NaN.NaN. With /33, calculateNetsize returns 0.5, and the block silently resolves one address below the input.
Reproduction Steps
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
const app = new cdk.App();
const stack = new cdk.Stack(app, 'S');
// Forgot the mask
new ec2.Vpc(stack, 'V', {
ipAddresses: ec2.IpAddresses.cidr('10.0.0.0'), // -> NaN.NaN.NaN.NaN is not a valid IP Address
});
Or, isolating the mask handling with no CDK app at all — this is the exact arithmetic from network-util.ts:
const mask = parseInt('10.0.0.0'.split('/')[1], 10); // NaN
const netsize = 2 ** (32 - mask); // NaN
// -> networkAddress NaN -> numToIp(NaN) -> 'NaN.NaN.NaN.NaN'
const mask33 = parseInt('10.0.0.0/33'.split('/')[1], 10); // 33
const netsize33 = 2 ** (32 - mask33); // 0.5 <-- half an address
Possible Solution
Validate the mask in the CidrBlock constructor before it reaches the arithmetic, and throw an UnscopedValidationError naming the mask.
Keeping parseInt semantics for the parse itself matters for backwards compatibility: '10.0.0.0/16abc' and '10.0.0.0/16.5' resolve to 16 today and synthesize successfully, so they must keep working. Only masks that cannot produce a usable block (NaN, < 0, > 32) should be rejected. Under that rule no input that synthesizes today changes behaviour:
| input |
before |
after |
10.0.0.0/16 |
10.0.0.0/16 |
10.0.0.0/16 |
0.0.0.0/0 |
0.0.0.0/0 |
0.0.0.0/0 |
10.0.0.1/32 |
10.0.0.1/32 |
10.0.0.1/32 |
10.0.0.0/16abc |
10.0.0.0/16 |
10.0.0.0/16 |
10.0.0.0/16.5 |
10.0.0.0/16 |
10.0.0.0/16 |
10.0.0.0/33 |
9.255.255.255/33 |
clear error |
10.0.0.0/-1 |
512.0.0.0 is not a valid IP Address |
clear error |
10.0.0.0 |
NaN.NaN.NaN.NaN is not a valid IP Address |
clear error |
The only rows that change are ones that already failed, or that produced a block CloudFormation would reject — so no feature flag is needed under the contributing guidance for validation changes.
Other Information
Related but distinct: #34784 covers misaligned base addresses being silently rounded up (10.0.3.1/28 → 10.0.3.16/28). This issue is about the mask, and is unaffected by that one.
Separately, and not proposed for change here: the octet check in NetworkUtils.validIp uses parseInt per octet, so '1.2.3.1e2/16' is accepted and synthesizes a VPC with CidrBlock 1.3.0.0/16. Tightening that would reject input that currently deploys, so it would need a feature flag — I've left it out to keep this to one concern.
CDK CLI Version
N/A (synthesis-time, framework only)
Framework Version
aws-cdk-lib 2.263.0
Node.js Version
v24.1.0
OS
macOS 15 (Darwin 25.2.0)
Language
TypeScript
Describe the bug
An invalid or missing CIDR mask is never validated, so it flows into the address arithmetic in
CidrBlockand surfaces as an error naming an IP address the caller never wrote — or, for a mask above 32, produces a nonsensical block with no error at all.Forgetting the mask is an easy mistake, and the resulting message gives no hint that the mask is the problem:
Nothing in that message points at the missing
/16.Full behaviour of
IpAddresses.cidr()on aws-cdk-lib 2.263.0:'10.0.0.0'(no mask)NaN.NaN.NaN.NaN is not a valid IP Address'10.0.0.0/'NaN.NaN.NaN.NaN is not a valid IP Address'10.0.0.0/abc'NaN.NaN.NaN.NaN is not a valid IP Address'10.0.0.0/-1'512.0.0.0 is not a valid IP Address'10.0.0.0/33'9.255.255.255/33The
/33row is the worst of these:new CidrBlock('10.0.0.0/33')returns a block whosecidris9.255.255.255/33, an address outside the range the caller asked for, and synthesis continues. The failure only appears later — either as a confusing downstream error (1 of /24 exceeds remaining space of 9.255.255.255/33) or at deploy time from CloudFormation.Expected Behavior
An invalid mask should fail fast with an error that names the mask as the problem and states the accepted range, per the repo's error-message guidance (include the wrong value, the expected values, and what to change). Something like:
Current Behavior
The mask is read with
parseIntand used without any validation:With no mask,
parseInt(undefined, 10)isNaN, socalculateNetsizereturns2 ** (32 - NaN)=NaN,networkAddressbecomesNaN, andnumToIpfinally reportsNaN.NaN.NaN.NaN. With/33,calculateNetsizereturns0.5, and the block silently resolves one address below the input.Reproduction Steps
Or, isolating the mask handling with no CDK app at all — this is the exact arithmetic from
network-util.ts:Possible Solution
Validate the mask in the
CidrBlockconstructor before it reaches the arithmetic, and throw anUnscopedValidationErrornaming the mask.Keeping
parseIntsemantics for the parse itself matters for backwards compatibility:'10.0.0.0/16abc'and'10.0.0.0/16.5'resolve to16today and synthesize successfully, so they must keep working. Only masks that cannot produce a usable block (NaN,< 0,> 32) should be rejected. Under that rule no input that synthesizes today changes behaviour:10.0.0.0/1610.0.0.0/1610.0.0.0/160.0.0.0/00.0.0.0/00.0.0.0/010.0.0.1/3210.0.0.1/3210.0.0.1/3210.0.0.0/16abc10.0.0.0/1610.0.0.0/1610.0.0.0/16.510.0.0.0/1610.0.0.0/1610.0.0.0/339.255.255.255/3310.0.0.0/-1512.0.0.0 is not a valid IP Address10.0.0.0NaN.NaN.NaN.NaN is not a valid IP AddressThe only rows that change are ones that already failed, or that produced a block CloudFormation would reject — so no feature flag is needed under the contributing guidance for validation changes.
Other Information
Related but distinct: #34784 covers misaligned base addresses being silently rounded up (
10.0.3.1/28→10.0.3.16/28). This issue is about the mask, and is unaffected by that one.Separately, and not proposed for change here: the octet check in
NetworkUtils.validIpusesparseIntper octet, so'1.2.3.1e2/16'is accepted and synthesizes a VPC with CidrBlock1.3.0.0/16. Tightening that would reject input that currently deploys, so it would need a feature flag — I've left it out to keep this to one concern.CDK CLI Version
N/A (synthesis-time, framework only)
Framework Version
aws-cdk-lib 2.263.0
Node.js Version
v24.1.0
OS
macOS 15 (Darwin 25.2.0)
Language
TypeScript