Skip to content

Bug: No collision detection between dimension keys, metric names, and metadata keys in EMF output #5208

@svozza

Description

@svozza

Expected Behavior

The library should prevent collisions between dimension keys, metric names, and metadata keys since they all share the same top-level namespace in the EMF JSON. Per the EMF spec, dimension targets MUST be strings and metric targets MUST be numbers — they cannot coexist under the same key.

Current Behavior

In serializeMetrics() at Metrics.ts:747-769, the EMF output is constructed by spreading objects in this order:

return {
  _aws: { ... },
  ...defaultDimensions,     // 1st
  ...dimensions,            // 2nd
  ...dimensionSetsFlat,     // 3rd
  ...metricValues,          // 4th — overwrites dimensions
  ...this.#metadataStore.getAll(),  // 5th — overwrites metrics
};

Since these keys share the same namespace, collisions can silently corrupt the output:

  • A metric named environment overwrites a dimension environment: 'prod' with a number — CloudWatch silently loses the dimension.
  • Metadata with key request_count overwrites a metric request_count: 42 with a string — CloudWatch fails to parse the metric.

Code snippet

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

const metrics = new Metrics({ namespace: 'test' });

// Problem 1: metric overwrites dimension
metrics.addDimension('environment', 'prod');
metrics.addMetric('environment', MetricUnit.Count, 1);
const output1 = metrics.serializeMetrics();
console.log(output1.environment);
// ❌ Outputs: 1 (number) — the dimension string 'prod' was silently overwritten

// Problem 2: metadata overwrites metric
const metrics2 = new Metrics({ namespace: 'test' });
metrics2.addMetric('request_count', MetricUnit.Count, 42);
metrics2.addMetadata('request_count', 'not-a-number');
const output2 = metrics2.serializeMetrics();
console.log(output2.request_count);
// ❌ Outputs: 'not-a-number' (string) — the numeric metric value 42 was overwritten

Steps to Reproduce

Metric overwrites dimension:

  1. Create a Metrics instance
  2. Add a dimension environment: 'prod'
  3. Add a metric named environment
  4. Call serializeMetrics()output.environment is 1, not 'prod'

Metadata overwrites metric:

  1. Create a Metrics instance
  2. Add a metric request_count with value 42
  3. Add metadata with key request_count
  4. Call serializeMetrics()output.request_count is 'not-a-number', not 42

Possible Solution

Maintain a Set of reserved keys and check against it in storeMetric() and addMetadata():

// In storeMetric():
const dimensionKeys = new Set([
  ...Object.keys(this.#dimensionsStore.getDimensions()),
  ...Object.keys(this.#dimensionsStore.getDefaultDimensions()),
]);
if (dimensionKeys.has(name)) {
  throw new Error(`Metric name "${name}" conflicts with an existing dimension key`);
}

// In addMetadata():
if (this.#metricsStore.getMetric(key) !== undefined) {
  throw new Error(`Metadata key "${key}" conflicts with an existing metric name`);
}

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 implementation

    Type

    No type

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions