core: acknowledgeWarning() before addWarningV2() on the same construct does not suppress the warning
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the bug
`Annotations.of(scope).acknowledgeWarning(id)` is documented as subtree-scoped ("The acknowledgement will apply to all child scopes"). However, if you acknowledge a warning on a construct **before** a warning with that same id is emitted on the **same** construct, the acknowledgement is ignored and the warning is still emitted.
The reverse order (emit first, then acknowledge) works, and acknowledging on an **ancestor** works for descendants in either order. Only the "acknowledge-then-emit on the same construct" case is broken. `acknowledgeInfo()` / `addInfoV2()` share the same lookup and have the same bug.
### Expected Behavior
Acknowledging a warning id on construct `C` suppresses a later `addWarningV2` with that id emitted on `C` itself (consistent with the documented subtree semantics and with the ancestor/child cases).
### Current Behavior
The warning is emitted anyway. Synthesizing the app below yields a warning on `/S1/C1`:
```
[ { path: '/S1/C1', message: 'You should know this! [ack: MESSAGE]' } ]
```
### Reproduction Steps
```ts
import { App, Stack } from 'aws-cdk-lib';
import { Annotations } from 'aws-cdk-lib';
import { Construct } from 'constructs';
const app = new App();
const stack = new Stack(app, 'S1');
const c1 = new Construct(stack, 'C1');
// Acknowledge FIRST, then emit on the SAME construct
Annotations.of(c1).acknowledgeWarning('MESSAGE', 'ack');
Annotations.of(c1).addWarningV2('MESSAGE', 'You should know this!');
app.synth(); // warning is still present on /S1/C1 (expected: suppressed)
```
### Root Cause
In `packages/aws-cdk-lib/core/lib/annotations.ts`, the private `Acknowledgements.searchPaths()` is documented as returning the node's own full path plus its ancestor prefixes (`Given 'a/b/c', return ['a/b/c', 'a/b', 'a']`), but the loop only pushes a prefix when it finds a `/`, so the final segment — the construct's **own** full path — is never included. It actually returns `['a/b', 'a']`.
`Acknowledgements.add()` records the ack under the construct's exact path, while `addWarningV2` gates emission on `has()`, which only checks the paths from `searchPaths()`. Because the construct's own path is excluded, the ack recorded on `C` is invisible to a warning emitted on `C`. Emitting on a **child** works because the child's `searchPaths()` includes `C` as an ancestor; the emit-then-ack order works because `acknowledgeWarning` also calls the self-inclusive `removeWarningDeep()`.
### Possible Solution
Make `searchPaths()` include the node's own full path (matching its docstring). Fix + regression tests in progress.
### Environment
- **CDK CLI Version:** n/a (framework behavior, reproduced against `main`)
- **Framework Version:** aws-cdk-lib (main)
- **Node.js Version:** n/a
- **OS:** n/a
- **Language:** TypeScript
### This is 🐛 Bug Report
Contributor guide
Research direction
Start in packages/aws-cdk-lib/core/lib/annotations.ts and read Acknowledgements.searchPaths(), then trace how addWarningV2() uses has(). Add regression coverage for acknowledging before emitting on the same construct, including the related info path, and confirm synthesis produces no warning for the acknowledged id.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cloud
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 75/100