Skip to content

treeifyError throws TypeError when an issue path contains a key inherited from Object.prototype (e.g. toString) #6070

@JuiceBlynskyi

Description

@JuiceBlynskyi

treeifyError throws a TypeError whenever a validation issue's path contains a segment whose name matches a property inherited from Object.prototype (for example toString, valueOf, constructor, or hasOwnProperty).

Minimal reproduction:

import zod, { treeifyError } from 'zod';

const schema = zod.object({ toString: zod.string() });
const result = schema.safeParse({}); // success: false

if (!result.success) {
    treeifyError(result.error); // throws TypeError
}

The parse correctly fails: {}.toString resolves to the inherited Object.prototype.toString function, so the toString field is reported as invalid_type (expected string, received function) with path: ["toString"]. That part is expected. The bug is that treeifyError then throws while building the tree:

node_modules/zod/v4/core/errors.js:113
                        curr.errors.push(mapper(issue));
                                    ^
TypeError: Cannot read properties of undefined (reading 'push')
    at processError (node_modules/zod/v4/core/errors.js:113:37)
    at treeifyError (node_modules/zod/v4/core/errors.js:120:5)

Root cause:

treeifyError builds the tree using a plain object as a dictionary:

curr.properties ?? (curr.properties = {});
(_a = curr.properties)[el] ?? (_a[el] = { errors: [] });
curr = curr.properties[el];

When the path segment el is an Object.prototype key, curr.properties[el] returns the inherited member (e.g. the toString function) instead of undefined. The ?? short-circuits, so the node is never created, and curr is assigned the inherited function which has no errors array. The following curr.errors.push(...) then throws.
A null-prototype object (Object.create(null)) or a Map for properties, or an Object.hasOwn guard, would fix it.

Environment:
zod: 4.4.3
Node.js: v24.15.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions