diff --git a/api/config/__tests__/parsers.spec.js b/api/config/__tests__/parsers.spec.js index 5c6953ce02..88ee977604 100644 --- a/api/config/__tests__/parsers.spec.js +++ b/api/config/__tests__/parsers.spec.js @@ -43,6 +43,20 @@ describe('formatConsoleMeta', () => { expect(meta).toBe('{"userField":"keep"}'); }); + it('drops numeric-index-like keys (splat artifacts from primitive args)', () => { + const meta = formatConsoleMeta({ + level: 'warn', + message: 'Unhandled step:', + timestamp: 'ts', + 0: 'f', + 1: 'o', + 2: 'o', + realField: 'real', + }); + + expect(meta).toBe('{"realField":"real"}'); + }); + it('drops empty, null, undefined, function, and symbol values', () => { const meta = formatConsoleMeta({ level: 'warn', diff --git a/api/config/parsers.js b/api/config/parsers.js index 056448c23d..10516a9ec4 100644 --- a/api/config/parsers.js +++ b/api/config/parsers.js @@ -110,6 +110,16 @@ function extractMetaObject(source) { if (RESERVED_LOG_KEYS.has(key) || key.startsWith('_')) { continue; } + /* + * Skip numeric-index-like keys. When a caller passes a primitive as + * the second argument to `logger.warn/error`, `format.splat()` can + * leave character-index keys ("0", "1", ...) on `info`. Those are + * synthetic splat artifacts, not real metadata, and would otherwise + * produce noisy output now that warn/error share this path. + */ + if (/^\d+$/.test(key)) { + continue; + } const value = source[key]; if (value === undefined || value === null || value === '') { continue;