aws / aws/jsii

StackOverflowError with Java when overriding Stack.allocateLogicalId

Open
#4,656 2 comments 0 reactions 0 assignees View on GitHub
bug effort/medium p2
Dominant language
TypeScript
Stars
2.9k
Forks
267
Avg merge
1d 25m
Merged PRs (30d)
14

Description

### Describe the bug

CDK with Java produces `StackOverflowError`s when overriding `Stack.allocateLogicalId` (and having many resources in the stack).

### Regression Issue

- [ ] Select this option if this issue appears to be a regression.

### Last Known Working CDK Version

_No response_

### Expected Behavior

Overriding `Stack.allocateLogicalId` should not cause a `StackOverflowError`.

### Current Behavior

There is a StackOverflowError when synthesizing the app:

Exception in thread "main" java.lang.StackOverflowError
at software.amazon.jsii.JsiiEngine.invokeMethod(JsiiEngine.java:482)
at software.amazon.jsii.JsiiEngine.invokeCallbackMethod(JsiiEngine.java:461)
at software.amazon.jsii.JsiiEngine.handleCallback(JsiiEngine.java:404)
at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:171)
at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
...
at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:121)
at software.amazon.jsii.JsiiRuntime.processCallbackResponse(JsiiRuntime.java:197)
node:events:497
throw er; // Unhandled 'error' event
^
Error: write EPIPE
at afterWriteDispatched (node:internal/stream_base_commons:159:15)
at writeGeneric (node:internal/stream_base_commons:150:3)
at Socket._writeGeneric (node:net:958:11)
at Socket._write (node:net:970:8)
Error: write EPIPE
at writeOrBuffer (node:internal/streams/writable:572:12)
at _write (node:internal/streams/writable:501:10)
at Writable.write (node:internal/streams/writable:510:10)
at Socket.ondata (node:internal/streams/readable:1009:22)
at Socket.emit (node:events:519:28)
at addChunk (node:internal/streams/readable:561:12)
Emitted 'error' event on Socket instance at:
at Socket.onerror (node:internal/streams/readable:1028:14)
at Socket.emit (node:events:519:28)
at emitErrorNT (node:internal/streams/destroy:170:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
errno: -32,
code: 'EPIPE',
syscall: 'write'
}

### Reproduction Steps

Put the following code in file call `Main.java` and run it with arguments "true" and "250" to create 250 buckets in a stack which overrides `Stack.allocateLogicalId`. This should reproduce the issue.

import org.jetbrains.annotations.NotNull;
import software.amazon.awscdk.App;
import software.amazon.awscdk.CfnElement;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.services.s3.Bucket;
import software.amazon.awscdk.services.s3.BucketProps;
import software.constructs.Construct;

public class Main {

/**
* Creates an app with a single stack which just has a lot of buckets.
* arg 1: whether to use a stack which overrides Stack.allocateLogicalId ("true" or "false")
* arg 2: the number of buckets to create
*/
public static void main(final String[] args) {
boolean overrideAllocateLogicalId = Boolean.parseBoolean(args[0]);
int bucketCount = Integer.parseInt(args[1]);

App app = new App();
Stack stack = overrideAllocateLogicalId ? new AllocateLogicalIdOverridingStack(app, bucketCount) : new AllocateLogicalIdNotOverridingStack(app, bucketCount);
System.out.println("Using " + stack.getClass().getName() + " to create " + bucketCount + " buckets.");
app.synth();
System.out.println("Successfully synthesized app.");
}

private abstract static class AbstractStack extends Stack {
protected AbstractStack(Construct scope, int bucketCount) {
super(scope);
for (int i = 0; i < bucketCount; i++) {
new Bucket(this, "bucket" + i, BucketProps.builder().bucketName("bucket" + i).enforceSsl(true).build());
}
}
}

private static class AllocateLogicalIdNotOverridingStack extends AbstractStack {
private AllocateLogicalIdNotOverridingStack(Construct scope, int bucketCount) {
super(scope, bucketCount);
}
}

private static class AllocateLogicalIdOverridingStack extends AbstractStack {
private AllocateLogicalIdOverridingStack(Construct scope, int bucketCount) {
super(scope, bucketCount);
}

@Override
public @NotNull String allocateLogicalId(@NotNull CfnElement cfnElement) {
// could allocate special ids here
return super.allocateLogicalId(cfnElement);
}
}
}

### Possible Solution

Not overriding `Stack.allocateLogicalId` fixes the issue. This can be verified by running the example code with "false" and "250".

Adding JVM parameter `-Xss2m` to increase the stack size also fixes the issue. This is an easy work around. But if this is really necessary (in bigger stacks) and if there are any limits one should be aware of, especially when overriding `Stack.allocateLogicalId`, they should be documented in the respective [Javadoc](https://docs.aws.amazon.com/cdk/api/v2/java/software/amazon/awscdk/Stack.html#allocateLogicalId(software.amazon.awscdk.CfnElement)).

### Additional Information/Context

The reason there is `.enforceSsl(true)` on the bucket (in example code) is that the problem was not reproducible without any bucket settings.

### CDK CLI Version

2.160.0

### Framework Version

2.160.0

### Node.js Version

v22.9.0

### OS

Linux

### Language

Java

### Language Version

Java 21 - Corretto-21.0.4.7.1 (build 21.0.4+7-LTS)

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the provided Main.java reproduction, running it with true and false and 250 buckets to compare synthesis behavior. Trace the repeated JsiiEngine and JsiiRuntime callback frames, then verify that overriding allocateLogicalId can synthesize the large stack without StackOverflowError while preserving the non-overridden case.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, node.js, typescript
Domain
developer-experience, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.