Skip to content

Bug: State leaks when throwOnEmptyMetrics throws — dimensions and metadata carry over to next publish #5207

@svozza

Description

@svozza

Expected Behavior

When publishStoredMetrics() throws due to throwOnEmptyMetrics, the internal state (dimensions, metadata) should still be cleaned up. A subsequent publishStoredMetrics() call should start with a clean slate.

Current Behavior

In publishStoredMetrics() at Metrics.ts:603-622:

if (!this.disabled) {
  const emfOutput = this.serializeMetrics();  // ← throws here (throwOnEmptyMetrics)
  hasMetrics && this.console.log(JSON.stringify(emfOutput));
}

this.clearMetrics();      // ← never reached
this.clearDimensions();   // ← never reached
this.clearMetadata();     // ← never reached

When serializeMetrics() throws (due to throwOnEmptyMetrics), the cleanup code on lines 618-620 never runs. Dimensions and metadata from the failed publish are retained and leak into the next publishStoredMetrics() call.

Code snippet

import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';

const metrics = new Metrics({ namespace: 'test' });
metrics.setThrowOnEmptyMetrics(true);
metrics.addDimension('request_id', 'abc-123');
metrics.addMetadata('trace_id', 'xyz-789');

// First publish throws — no metrics added
try {
  metrics.publishStoredMetrics();
} catch {
  // expected
}

// Second publish — dimensions and metadata from the failed publish leak through
metrics.setThrowOnEmptyMetrics(false);
metrics.addMetric('test', MetricUnit.Count, 1);
metrics.publishStoredMetrics();
// ❌ The EMF output contains request_id: 'abc-123' and trace_id: 'xyz-789'
//    even though they should have been cleared

Steps to Reproduce

  1. Create a Metrics instance, enable throwOnEmptyMetrics
  2. Add a dimension and metadata, but no metrics
  3. Call publishStoredMetrics() — it throws
  4. Catch the error, disable throwOnEmptyMetrics, add a metric, publish again
  5. Observe the leaked dimension and metadata in the second EMF output

Possible Solution

Wrap the serialize/emit block in try/finally to guarantee cleanup:

try {
  if (!this.disabled) {
    const emfOutput = this.serializeMetrics();
    hasMetrics && this.console.log(JSON.stringify(emfOutput));
  }
} finally {
  this.clearMetrics();
  this.clearDimensions();
  this.clearMetadata();
}

Powertools for AWS Lambda (TypeScript) version

2.33.0

AWS Lambda function runtime

22.x

Packaging format used

npm


Disclaimer: After creating an issue, please wait until it is triaged and confirmed by a maintainer before implementing it. This will reduce amount of rework and the chance that a pull request gets rejected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingconfirmedThe scope is clear, ready for implementationgood-first-issueSomething that is suitable for those who want to start contributing

    Type

    No type

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions