Skip to content

fix(splat): stop mutating the shared info[SPLAT] array - #363

Open
yfwmaniish wants to merge 1 commit into
winstonjs:masterfrom
yfwmaniish:fix/splat-shared-array-mutation
Open

yfwmaniish wants to merge 1 commit into
winstonjs:masterfrom
yfwmaniish:fix/splat-shared-array-mutation

Conversation

@yfwmaniish

Copy link
Copy Markdown

Fixes winstonjs/winston#2365.

What

Both code paths in `Splatter` separate "meta" (extra arguments beyond what the format string's tokens consume) from "splat" by calling `.splice()` directly on `info[SPLAT]`:

```js
// _splat() - tokens path
const metas = extraSplat < 0
? splat.splice(extraSplat, -1 * extraSplat)
: [];
...
info.message = util.format(msg, ...splat); // splat has now been mutated

// transform() - no-tokens path
const metas = splat.length > 1
? splat.splice(0)
: splat;
```

`info[SPLAT]` is an array reference. winston shallow-clones `info` once per transport (each transport's own format pipeline gets its own `info` object), but a shallow clone still shares the same `info[SPLAT]` array across all of them. So the first transport's `.splice()` mutates that shared array in place - by the time the second transport's format pipeline runs `splat()` over its own `info` object, `info[SPLAT]` is already truncated (or emptied entirely), and the meta winston attached to the log call is silently gone.

Reproduces exactly as described in the issue: two `Console` transports with `format.combine(format.splat(), format.json())`, only the first one gets `extraData`.

Fix

Both call sites now read the same values via `.slice()` instead of `.splice()` - same values extracted, but the original array (and thus `info[SPLAT]`) is left untouched. _splat()'s local splat (used for util.format) is likewise derived via .slice() instead of relying on the post-splice, already-mutated array.

Testing

Added two regression tests (tokens path and no-tokens path), each simulating two transports as two separate `info` objects sharing one `SPLAT` array reference - reusing the same `info` object across both transform() calls wouldn't actually catch this bug (a stale property surviving on the same object would mask it), so the test mirrors winston's real shallow-clone-per-transport shape.

  • Negative control: reverted just `splat.js`, kept the tests - both fail with the meta reading `undefined` on the second transport, matching the real bug. Restored.
  • Full suite: `mocha test/*.test.js` - 171/171 passing.
  • `eslint` clean on both changed files (verified against the git-staged/LF-normalized content; this Windows checkout has `core.autocrlf` converting the whole working tree to CRLF locally, which is unrelated noise - confirmed by checking an untouched file too).

Separate from #362 (which fixes a different bug - primitive metas getting char-spread via `Object.assign`, tracked as #109). This PR doesn't touch that code path.

Copilot AI lite review requested due to automatic review settings September 1, 2026 08:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Both code paths in Splatter (the tokens path in _splat(), and the
no-tokens path in transform()) separated "meta" from "splat" by
calling .splice() on info[SPLAT] directly. Since info[SPLAT] is an
array reference, and winston shallow-clones info per transport
(giving each transport's format pipeline its own info object but the
same info[SPLAT] array), the first transport's splice mutated that
shared array in place - so every subsequent transport saw an
already-truncated (or emptied) splat, and silently lost the meta
winston had attached to the log call.

Both call sites now extract the same values via slice() instead,
which reads from the array without mutating it.

Fixes winstonjs/winston#2365.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Splat Mutates Info Object

2 participants