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
    No fields configured for issues without a type.

    Projects

    Status

    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions