Skip to content

Web/runtime built-ins lack Symbol.toStringTag: Object.prototype.toString.call(new URLSearchParams()) is [object Object] (URL, Headers, Blob, Request, Response, FormData, TextEncoder, AbortController, Event, EventTarget…) #10555

Description

@proggeramlug

Found by the package audit (while fixing #10434) on Perry 7661bc0 (v0.5.1589), Linux x64. Perry's Web API
objects have no Symbol.toStringTag, so Object.prototype.toString.call(x) yields [object Object] and
x[Symbol.toStringTag] is undefined. ES built-ins (Map, Promise, ArrayBuffer, DataView) produce the right
[object …] string but also report undefined for the Symbol.toStringTag property itself.

Reproduction

main.ts:

const vals: [string, any][] = [
  ["URL", new URL("http://x/")], ["URLSearchParams", new URLSearchParams("a=1")], ["Headers", new Headers()],
  ["TextEncoder", new TextEncoder()], ["TextDecoder", new TextDecoder()], ["Blob", new Blob(["a"])],
  ["AbortController", new AbortController()], ["AbortSignal", new AbortController().signal], ["Map", new Map()],
  ["Promise", Promise.resolve()], ["ArrayBuffer", new ArrayBuffer(1)], ["Uint8Array", new Uint8Array(1)],
  ["Request", new Request("http://x/")], ["Response", new Response("x")], ["FormData", new FormData()],
  ["EventTarget", new EventTarget()], ["Event", new Event("x")], ["DataView", new DataView(new ArrayBuffer(1))],
];
for (const [n, v] of vals) console.log(n, Object.prototype.toString.call(v), String(v?.[Symbol.toStringTag]));
node main.ts
PERRY_NO_AUTO_OPTIMIZE=1 perry compile main.ts -o main && ./main

Expected (Node 26.5.1)

URL [object URL] URL
URLSearchParams [object URLSearchParams] URLSearchParams
Headers [object Headers] Headers
TextEncoder [object TextEncoder] TextEncoder
TextDecoder [object TextDecoder] TextDecoder
Blob [object Blob] Blob
AbortController [object AbortController] AbortController
AbortSignal [object AbortSignal] AbortSignal
Map [object Map] Map
Promise [object Promise] Promise
ArrayBuffer [object ArrayBuffer] ArrayBuffer
Uint8Array [object Uint8Array] Uint8Array
Request [object Request] Request
Response [object Response] Response
FormData [object FormData] FormData
EventTarget [object EventTarget] EventTarget
Event [object Event] Event
DataView [object DataView] DataView

Actual (Perry)

URL [object Object] undefined
URLSearchParams [object Object] undefined
Headers [object Object] undefined
TextEncoder [object Object] undefined
TextDecoder [object Object] undefined
Blob [object Object] undefined
AbortController [object Object] undefined
AbortSignal [object Object] undefined
Map [object Map] undefined
Promise [object Promise] undefined
ArrayBuffer [object ArrayBuffer] undefined
Uint8Array [object Uint8Array] Uint8Array
Request [object Object] undefined
Response [object Object] undefined
FormData [object Object] undefined
EventTarget [object Object] undefined
Event [object Object] undefined
DataView [object DataView] undefined

Impact

Object.prototype.toString.call(x) is the standard cross-realm type check used by utility and HTTP libraries
(kindOf, isURLSearchParams, isFormData, isBlob, lodash baseGetTag). axios 1.19.0 decides how to serialize a
request body this way: a URLSearchParams body is sent as JSON instead of application/x-www-form-urlencoded.
Similar branches exist in form-data, undici, node-fetch, got, superagent and lodash.

Notes

  • Fix shape: define Symbol.toStringTag (non-enumerable, non-writable, configurable string) on each built-in's
    prototype, and have the Object.prototype.toString fast path for ES built-ins also expose the property via
    x[Symbol.toStringTag]. A test that sweeps every global constructor exposed by the runtime against Node's tags
    would catch the rest of the population.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindings

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions