CDK scaffold bin/cdk.ts async entry point breaks cdk ls/synth/deploy
- Dominant language
- TypeScript
- Stars
- 283
- Forks
- 95
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 183
Description
## Bug Description
The generated `bin/cdk.ts` from `agentcore create` uses an `async main()` function with `await configIO.readProjectSpec()` and `await configIO.readAWSDeploymentTargets()`. This causes `cdk ls`, `cdk synth`, and `cdk deploy` to silently produce zero stacks with exit code 0.
## Root Cause
The CDK `App` class has an `autoSynth` property that defaults to `true` when `CDK_OUTDIR` is set (i.e., when run by the CDK CLI). `autoSynth` uses Node.js `process.on('beforeExit')` to call `app.synth()` automatically. However, `beforeExit` fires when the **synchronous** event loop drains — **before** `async main()` completes.
Timeline when CDK CLI runs the app:
1. CDK CLI spawns `node dist/bin/cdk.js` with `CDK_OUTDIR` set to a temp dir
2. `main().catch(...)` starts the async function but returns immediately (it's a promise)
3. Synchronous event loop drains → `beforeExit` fires → `autoSynth` calls `app.synth()` on an **empty App** (stacks are inside the unresolved `main()`)
4. CDK CLI reads the empty cloud assembly → zero stacks
The explicit `app.synth()` at the end of `main()` eventually runs, but by then the CDK CLI has already read the output.
Reference: [CDK AppProps.autoSynth docs](https://docs.aws.amazon.com/cdk/v2/guide/configure-synth.html) — "Automatically call synth() before the program exits. Default: true if running via CDK CLI (CDK_OUTDIR is set)"
## Reproduction
```bash
# 1. Create a fresh project
agentcore create --name myagent --defaults
# 2. Add a deployment target
cat > myagent/agentcore/aws-targets.json << 'EOF'
[{"name": "main", "account": "123456789012", "region": "us-east-1"}]
EOF
# 3. Install and compile
cd myagent/agentcore/cdk
npm install
npx tsc
# 4. List stacks — expect "AgentCore-myagent-main", get nothing
npx cdk ls
# Output: (empty, exit code 0)
# 5. Verify the app DOES create stacks when run directly
node -e "
const { AgentCoreStack } = require('./dist/lib/cdk-stack');
const cdk = require('aws-cdk-lib');
const app = new cdk.App();
console.log('Stacks:', app.node.children.length);
"
# This also shows 0 because stacks are inside async main()
```
## Expected Behavior
`npx cdk ls` should output:
```
AgentCore-myagent-main
```
## Actual Behavior
`npx cdk ls` outputs nothing (empty stdout, exit code 0). `cdk synth` also exits 0 but produces no template. `cdk deploy` would silently deploy nothing.
## Proposed Fix
Replace async `ConfigIO` calls with synchronous `fs.readFileSync` in the scaffold template (`src/assets/cdk/bin/cdk.ts`):
```typescript
#!/usr/bin/env node
import { AgentCoreStack } from '../lib/cdk-stack';
import type { AgentCoreProjectSpec, AwsDeploymentTarget } from '@aws/agentcore-cdk';
import { App, type Environment } from 'aws-cdk-lib';
import * as path from 'path';
import * as fs from 'fs';
// Synchronous reads — CDK CLI requires stacks registered before process exits.
// Async ConfigIO calls race with the CDK CLI's autoSynth (beforeExit hook).
const configRoot = path.resolve(__dirname, '..', '..', '..');
const spec: AgentCoreProjectSpec = JSON.parse(
fs.readFileSync(path.join(configRoot, 'agentcore.json'), 'utf8'),
);
const targets: AwsDeploymentTarget[] = JSON.parse(
fs.readFileSync(path.join(configRoot, 'aws-targets.json'), 'utf8'),
);
if (targets.length === 0) {
throw new Error('No deployment targets configured.');
}
const app = new App();
for (const target of targets) {
new AgentCoreStack(app, `AgentCore-${spec.name}-${target.name}`, {
spec,
env: { account: target.account, region: target.region },
});
}
app.synth();
```
The `agentcore.json` and `aws-targets.json` files are small local JSON — there's no benefit to reading them asynchronously, and doing so breaks CDK CLI compatibility.
## Environment
- `@aws/agentcore` CLI: 0.8.0
- `@aws/agentcore-cdk`: 0.1.0-alpha.17
- `aws-cdk-lib`: 2.189.1
- CDK CLI: 2.1100.1
- Node.js: v22.x
- OS: macOS (Darwin 25.3.0)
Contributor guide
Assessment
This issue has not been assessed yet.