Skip to content

fix: construct errors are rendered in a messy way#37290

Merged
mergify[bot] merged 8 commits intomainfrom
huijbers/validation-error-display
Mar 19, 2026
Merged

fix: construct errors are rendered in a messy way#37290
mergify[bot] merged 8 commits intomainfrom
huijbers/validation-error-display

Conversation

@rix0rrr
Copy link
Copy Markdown
Contributor

@rix0rrr rix0rrr commented Mar 19, 2026

In the past, Construct Errors (ValidationError, UnscopedValidationError, AssumptionError) had a number of downsides:

  • If the construct path was not set (even for an UnscopedValidationError) the path would be rendered as at path [undefined].
  • For multiline error messages, parts of the error would be repeated.
  • (When used as a library) Errors would include the JavaScript line of code throwing the error, but a production CDK build is minified so it would just be giant line of noise.

In this PR:

  • Render both the call stack and the construct path visually distinct.
  • Render the construct path visually using a tree.
  • Only render the construct path if available (avoid rendering undefined for errors are not related to a construct)
  • Don't render the actual contents of the source line, to avoid a huge minified line.
  • In order to improve the readability of the stack trace, we render Just My Code (this is a Visual Code term): we hide stack traces belonging to library code, focusing only on user code.
  • Make the S3 Bucket Name validation error scoped (instead of unscoped)

Examples (see 👀 for what to pay attention to)

Scoped error (in CDK repo)

BEFORE

/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:2266
      throw new UnscopedValidationError('InvalidBucketNameValue', `Invalid S3 bucket name (value: ${bucketName})${EOL}${errors.join(EOL)}`);
            ^
InvalidBucketNameValue: Invalid S3 bucket name (value: &*&*$)
Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 0)
Bucket name must start with a lowercase character or number (offset: 0)
Bucket name must end with a lowercase character or number (offset: 4)
👀    at path [/SomeStack/TargetBucket]

👀 Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 0)
👀 Bucket name must start with a lowercase character or number (offset: 0)
👀 Bucket name must end with a lowercase character or number (offset: 4)
    at Bucket.validateBucketName (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:2266:13)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:2319:12)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/lib/prop-injectable.ts:40:7)
    at Object.<anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:10:22)
    at Module._compile (node:internal/modules/cjs/loader:1761:14)
    at Module.m._compile (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1893:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1481:32)
    at Module._load (node:internal/modules/cjs/loader:1300:12)

AFTER

InvalidBucketNameValue: Invalid S3 bucket name (value: &*&*$)
Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 0)
Bucket name must start with a lowercase character or number (offset: 0)
Bucket name must end with a lowercase character or number (offset: 4)
    at validateBucketName (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:1003)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:1046)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/lib/prop-injectable.ts:31)
    at <anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:10)
👀    ...node internals, m._compile in ts-node, require.extensions.<computed> in ts-node...
👀  Relates to construct:
    <.> (constructs.Construct)
     └─ SomeStack (constructs.Construct)
         └─ TargetBucket (constructs.Construct)

Unscoped error (in CDK repo)

BEFORE

/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:24
throw new cdk.UnscopedValidationError('Blaaah', 'Some error');
      ^
Blaaah: Some error
👀    at path [undefined]

    at Object.<anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:24:7)
    at Module._compile (node:internal/modules/cjs/loader:1761:14)
    at Module.m._compile (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1893:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1481:32)
    at Module._load (node:internal/modules/cjs/loader:1300:12)
    at TracingChannel.traceSync (node:diagnostics_channel:328:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:245:24)
    at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)

AFTER

Blaaah: Some error
    at <anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:22)
👀   ...node internals, m._compile in ts-node, require.extensions.<computed> in ts-node...

When using CDK as a library

AFTER

InvalidBucketNameValue: Invalid S3 bucket name (value: &*&*$)
Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 0)
Bucket name must start with a lowercase character or number (offset: 0)
Bucket name must end with a lowercase character or number (offset: 4)
👀    ...new Bucket2 in aws-cdk-lib...
    at new ApiStack (/Users/huijbers/Temp/testcustomer2/lib/api-stack.ts:2)
    at <anonymous> (/Users/huijbers/Temp/testcustomer2/bin/repro.ts:3)
    at <anonymous> (/Users/huijbers/Temp/testcustomer2/bin/repro.ts:4)
    ...node internals, transformer in tsx...
Relates to construct:
    <.> (aws-cdk-lib.App)
     └─ ReproApiStack (aws-cdk-lib.Stack)
         └─ TargetBucket (aws-cdk-lib.aws_s3.Bucket)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

In the past, Construct Errors (`ValidationError`,
`UnscopedValidationError`, `AssumptionError`) had a number of downsides:

- If the construct path was not set (even for an
  `UnscopedValidationError`) the path would be rendered as ` in
  [undefined]`.
- For multiline error messages, parts of the error would be repeated.
- (When used as a library) Errors would include the JavaScript line of
  code throwing the error, but a production CDK build is minified so it
  would just be giant line of noise.

In this PR:

- Render both the call stack and the construct path visually distinct.
- Render the construct path visually using a tree.
- Only render the construct path if available (avoid rendering
  `undefined` for errors are not related to a construct)
- Don't render the actual contents of the source line, to avoid a huge
  minified line.
- In order to improve the readability of the stack trace, we render Just
  My Code (this is a Visual Code term): we hide stack traces belonging
  to library code, focusing only on user code.

Examples:

BEFORE

```
/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:2266
      throw new UnscopedValidationError('InvalidBucketNameValue', `Invalid S3 bucket name (value: ${bucketName})${EOL}${errors.join(EOL)}`);
            ^
InvalidBucketNameValue: Invalid S3 bucket name (value: &*&*$)
Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 0)
Bucket name must start with a lowercase character or number (offset: 0)
Bucket name must end with a lowercase character or number (offset: 4)
    at path [undefined]

Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 0)
Bucket name must start with a lowercase character or number (offset: 0)
Bucket name must end with a lowercase character or number (offset: 4)
    at Bucket.validateBucketName (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:2266:13)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:2319:12)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/lib/prop-injectable.ts:40:7)
    at Object.<anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:10:22)
    at Module._compile (node:internal/modules/cjs/loader:1761:14)
    at Module.m._compile (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1893:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1481:32)
    at Module._load (node:internal/modules/cjs/loader:1300:12)
```

AFTER

```
InvalidBucketNameValue: Invalid S3 bucket name (value: &*&*$)
Bucket name must only contain lowercase characters and the symbols, period (.) and dash (-) (offset: 0)
Bucket name must start with a lowercase character or number (offset: 0)
Bucket name must end with a lowercase character or number (offset: 4)
    at validateBucketName (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:1003)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/aws-s3/lib/bucket.ts:1046)
    at new Bucket (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/lib/prop-injectable.ts:31)
    at <anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:10)
    ...node internals, m._compile in ts-node, require.extensions.<computed> in ts-node...
```

BEFORE

```
/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:24
throw new cdk.UnscopedValidationError('Blaaah', 'Some error');
      ^
Blaaah: Some error
    at path [undefined]

    at Object.<anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:24:7)
    at Module._compile (node:internal/modules/cjs/loader:1761:14)
    at Module.m._compile (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1893:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1481:32)
    at Module._load (node:internal/modules/cjs/loader:1300:12)
    at TracingChannel.traceSync (node:diagnostics_channel:328:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:245:24)
    at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
```

AFTER

```
Blaaah: Some error
    at <anonymous> (/Users/huijbers/Workspaces/PublicCDK/aws-cdk4/packages/aws-cdk-lib/core/test/integ.error.ts:22)
    ...node internals, m._compile in ts-node, require.extensions.<computed> in ts-node...
```
@rix0rrr rix0rrr requested a review from a team as a code owner March 19, 2026 12:58
@github-actions github-actions bot added the p2 label Mar 19, 2026
@aws-cdk-automation aws-cdk-automation requested a review from a team March 19, 2026 12:58
@mergify mergify bot added the contribution/core This is a PR that came from AWS. label Mar 19, 2026
Copy link
Copy Markdown
Collaborator

@aws-cdk-automation aws-cdk-automation left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This review is outdated)

@rix0rrr rix0rrr added pr-linter/exempt-test The PR linter will not require test changes pr-linter/exempt-integ-test The PR linter will not require integ test changes labels Mar 19, 2026
@rix0rrr rix0rrr changed the title fix: Construct errors are rendered in a messy way fix: construct errors are rendered in a messy way Mar 19, 2026
@aws-cdk-automation aws-cdk-automation dismissed their stale review March 19, 2026 13:10

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

@mergify
Copy link
Copy Markdown
Contributor

mergify bot commented Mar 19, 2026

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify
Copy link
Copy Markdown
Contributor

mergify bot commented Mar 19, 2026

Merge Queue Status

  • Entered queue2026-03-19 18:52 UTC · Rule: default-squash
  • Checks passed · in-place
  • Merged2026-03-19 23:24 UTC · at e2ced9e3bb9c7753b77d02235dccbac1724d54ff

This pull request spent 4 hours 31 minutes 31 seconds in the queue, including 44 minutes 46 seconds running CI.

Required conditions to merge

@mergify
Copy link
Copy Markdown
Contributor

mergify bot commented Mar 19, 2026

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@mergify mergify bot merged commit 5104256 into main Mar 19, 2026
18 of 19 checks passed
@mergify mergify bot deleted the huijbers/validation-error-display branch March 19, 2026 23:24
@github-actions
Copy link
Copy Markdown
Contributor

Comments on closed issues and PRs are hard for our team to see.
If you need help, please open a new issue that references this one.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

contribution/core This is a PR that came from AWS. p2 pr-linter/exempt-integ-test The PR linter will not require integ test changes pr-linter/exempt-test The PR linter will not require test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants