Skip to content

getLoggerContext crashes on pino >= 8 browser builds — "bindings is not a function" (breaks wagmi's prescribed pino security override) #273

Description

@nigelon11

Package: @walletconnect/logger@2.1.2

Summary

getLoggerContext (and everything built on it — generateChildLogger, formatChildLoggerContext) crashes at runtime in browsers when the underlying pino is v8 or newer:

TypeError: e.bindings is not a function

This currently fires for anyone who follows wagmi's own deprecation guidance on the walletConnect connector, which recommends overriding the vulnerable pino@7.11.0 to pino@10.0.0 (see the note in wagmi/connectors' walletConnect.d.ts). With that override in place, the very first EthereumProvider.init() throws as soon as the logger nests two child levels deep — i.e., WalletConnect is unusable in the browser on any secure pino.

Root cause

getLoggerContext duck-types the pino API like this (from logger/src/utils.ts, shown as shipped in dist/index.es.js):

function getLoggerContext(logger, customContextKey) {
  let context = "";
  return typeof logger.bindings === "undefined"
    ? (context = getBrowserLoggerContext(logger, customContextKey))
    : (context = logger.bindings().context || ""),
    context;
}

The assumption is: "if bindings exists, it's callable." That held for pino 7's browser build, where bindings was simply absent on browser loggers. From pino 8 onward, the browser build exposes bindings on child loggers as a plain data object, so the check passes and the call throws.

Empirical matrix (require('pino/browser.js'), then pino({}).child({}).bindings):

pino root bindings child bindings
7.11.0 undefined undefined
8.21.0 undefined object 💥
9.14.0 undefined object 💥
10.0.0 undefined object 💥

The crash surfaces on the second nesting level: the first generateChildLogger(root, …) works (root has no bindings), attaches the custom context key, and returns a pino child — the next generateChildLogger(child, …) then hits the object-valued bindings and calls it.

Minimal reproduction

// npm i pino@10 @walletconnect/logger
const pino = require("pino/browser.js"); // what bundlers resolve for browser targets
const { generateChildLogger, getDefaultLoggerOptions } = require("@walletconnect/logger");

const root = pino(getDefaultLoggerOptions({ level: "error" }));
const core = generateChildLogger(root, "core");       // ok
const relayer = generateChildLogger(core, "relayer"); // TypeError: core.bindings is not a function

Suggested fix (one line)

Tighten the duck-type so any non-callable bindings falls back to the browser-context mechanism the module already has:

-  return typeof logger.bindings === "undefined"
+  return typeof logger.bindings !== "function"
     ? (context = getBrowserLoggerContext(logger, customContextKey))
     : (context = logger.bindings().context || ""), context;

This is behavior-identical on pino 7 (absent → fallback) and on Node builds (callable → used), and restores browser operation on pino ≥ 8. We've been running exactly this as a pnpm patch in production and can confirm the WalletConnect connector initializes and pairs normally with pino@10 under it. Happy to open a PR if useful.

Impact / context

  • Anyone applying the remediation that wagmi's connector deprecation note prescribes ("@walletconnect/logger": { "pino": "10.0.0" } override) ships this crash to every WalletConnect user of their dapp.
  • Without the override, consumers are stuck on the vulnerable pino@7.11.0 — so today the choice is "vulnerable dependency" or "broken connector". Fixing the duck-type (or bumping the pino dependency here with the API updates) resolves the dilemma at the source.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions