Skip to content
Putting technology to work.
Insights to guide decisions and action.

Search articles

Zod 4.6.6: arrays over the size limit now fail immediately without checking elements

Table of contents · 7 items

Consider an ordering or quoting business system where an API limits the line items it receives with Zod's .max(), for example "up to 100 lines" (a hypothetical scenario). When a submission exceeds the limit, it is natural to assume it simply returns an error and stops.

However, up to Zod 4.6.5, even for arrays over the limit, it first validated every element and only then checked the count limit. When 100,000 invalid line items arrive, validation runs for all 100,000 lines, and "limit exceeded" is reported only after per-line errors have piled up.

Version 4.6.6, published on October 11, 2026, reversed this order. We summarize the results of running and comparing 4.6.5 and 4.6.7 locally, and what to check before upgrading.

What changed in 4.6.6

The fix commit (#6652) in the Zod repository (colinhacks/zod) includes the following description.

.max(), .length() and .size() ran after every element was walked, so an oversized input built one issue per bad element before the bound was reported. Leading size checks now reject the input first, both in the interpreter and in the compiled path.

The key points are as follows.

  • It applies to the limit checks (.max(), .length(), .size()) on Zod 4 arrays (z.array), z.set and z.map
  • Input over the limit fails before its elements are validated
  • It was applied to both normal validation and precompiled (z.compile()) validation

This fix is treated as the fix for the vulnerability CVE-2023-54404. The public advisory from VulnCheck, which registered the CVE (published October 1, 2026), lists the affected versions as "zod <= 4.6.5," identifies this commit as the fix (its ID before the history was rewritten was e06a510), and rates the severity as 8.2 (High) under CVSS v4.0. It describes how sending a large array to an array schema with no count limit can pile up per-element errors, exhaust memory and potentially crash the process. Issue #6643, which prompted the fix, also cites this CVE and reports that 4.6.5 is affected.

However, simply upgrading to 4.6.6 does not protect you. When closing the related Issue #1872, the maintainers explained that apps accepting external input need to limit it with a request body size limit or a schema-level cap such as .max(), and that since #6652, arrays with .max() are rejected before their elements are examined. Arrays without a cap such as .max() still accumulate validation work and errors for every element, even in 4.6.6 and later.

According to npm, 4.6.6 was published on October 11, 2026, and 4.6.7 on October 12 (both UTC). The repository history shows that the diff from 4.6.6 to 4.6.7 consists only of the version number, the publishing script and the development lockfile; the validation code is identical. 4.6.5 was published on September 13, and as of October 13, npm's latest pointed to 4.6.7.

Note that the minimum constraints .min() and .nonempty() are not checked up front. Arrays that fail the minimum still return both the element errors and the minimum error, as before.

Comparing the same input on 4.6.5 and 4.6.7

On Node.js 22.22.0 (Linux), we installed 4.6.5, 4.6.6 and 4.6.7 in separate directories and ran the same script. The schema is an array of line items with .max(100).

const Item = z.object({ sku: z.string(), qty: z.number().int().positive() });
const Body = z.object({ items: z.array(Item).max(100) });

The input is an array of 100,000 copies of { sku: 1, qty: -1 } (both fields invalid). We ran safeParse five times on each version.

Editorial team test results. Validating 100,000 invalid line items against an array with .max(100), 4.6.5 produced 200,001 errors, about 27.9 MB of error JSON and a median of about 369 ms, while 4.6.7 produced 1 error, 141 bytes and 0.04 ms. Even with 100,000 valid line items, 4.6.5 ran element validation 100,000 times and 4.6.7 ran it 0 times

Here is an excerpt of the run log.

zod 4.6.5
A 100k invalid items, .max(100): issues=200001 errorJSONbytes=27877921 median_ms=368.728
B 100k valid items,   .max(100): issues=1 elemRefineCalls/run=100000 median_ms=45.529
zod 4.6.7
A 100k invalid items, .max(100): issues=1 errorJSONbytes=141 median_ms=0.038
B 100k valid items,   .max(100): issues=1 elemRefineCalls/run=0 median_ms=0.335

The results show three things.

  1. In 4.6.5, the number of errors grew in proportion to the input. The 200,000 errors from 100,000 items × 2 fields plus 1 error for exceeding the cap made 200,001 errors, which came to about 27.9 MB after JSON.stringify. If you output errors as-is to logs or responses, that size and processing are added on top
  2. In 4.6.7, only the single cap-exceeded error remained. The error is code: "too_big", path: ["items"]
  3. Even with valid elements, 4.6.5 validated all of them. When we added a counter to the element's .refine(), it was called 100,000 times in a single validation on 4.6.5 and 0 times on 4.6.7

Times are the median of five runs in this environment and will change under different conditions. What matters is the difference in order of magnitude. We also compared a schema with z.compile() using the same input: 4.6.5 produced 200,001 errors and 4.6.7 produced 1.

Patterns that are not checked up front

Depending on how the schema is written, this change does not take effect. Here are the conditions we checked locally (results of counting element validations on 4.6.7 with 1,000 elements and input exceeding a cap such as .max(100)).

ApproachElement validation on 4.6.7
z.array(el).max(100)、.min(1).max(100)、.nonempty().max(100)0 times (checked first)
z.array(el).length(10), Zod Mini's .check(z.maxLength(100))0 times (checked first)
z.array(el).max(100).refine(...) / .transform(...) with .optional() attached0 times (checked first)
z.array(el).refine(...).max(100)1,000 times (checked afterward)
import { z } from "zod/v3" for z.array(el).max(100)1,000 times (checked afterward)

The key is the order of the checks. In the source, the checks attached to an array are examined from the start, and the set of checks evaluated up front stops at the first check that is not a size check (such as .refine()). This is because counting items after a check that rewrites the value (.overwrite()) could change the result, and the tests include an example of this.

The other case is the compatibility entry point zod/v3. This fix only changes the Zod 4 code, and schemas imported from zod/v3 still validated every element before checking the cap, even on 4.6.7.

Arrays without .max() have no size limit in any version to begin with. As a precaution against the CVE mentioned above as well, what these results suggest is to put a cap on arrays in API input and place it before .refine(). It is also worth checking whether your HTTP server limits request size (we did not test server settings this time).

Changes you will see in the UI and tests

Beyond lighter processing, the content of the returned errors also changes. We compared input that slightly exceeds the cap: an array with .max(2) containing three invalid line items.

4.6.5: items.0.sku / items.0.qty / items.1.sku / items.1.qty / items.2.sku / items.2.qty / items(too_big) の7件
4.6.7: items(too_big) の1件

From this, the following impacts are possible (the editorial team's assessment).

  • Per-row error display in forms. When a form is submitted with more rows than the limit, 4.6.5 showed each row's input mistakes together with "too many rows," but from 4.6.6 onward only the row-count error appears. Each row's errors first appear when the user removes rows and resubmits. Decide whether to align on-screen messages and guidance with this order
  • Tests that pin the error shape. Tests that compare the number or list of errors for over-the-cap input using snapshots will fail after upgrading to 4.6.6. This is an intended change, so update the expected values
  • If you put logic in element validation. If an element's .refine() or .transform() performs master data lookups, logging or similar work, it will no longer be called for over-the-cap input. Check this if you keep counting logic or side effects on the element side

We cover setups that revalidate form input on the server with Zod in How to build application forms whose fields change based on selections, and using Zod with Hono's validator in Best practices for building backend APIs with Hono.

Checklist for upgrading

  1. Check the versions in use. Use npm ls zod to see every version loaded by the app itself and its dependencies. Dependencies sometimes carry their own version of Zod
  2. List your array, Set and Map schemas and check whether they have .max(), .length() or .size(), and whether these come before .refine()
  3. If any schemas are imported from zod/v3, decide whether to migrate them to the Zod 4 style
  4. Add tests for over-the-cap input, or update the expected values of existing tests
  5. For screens with a row limit in forms, check what is displayed when the limit is exceeded

For the inventory, you can use searches like the following.

npm ls zod
grep -rnE "z\.(array|set|map)\(" src | grep -vE "\.(max|length|size)\("
grep -rn "from \"zod/v3\"\|from 'zod/v3'" src

The first line finds versions, the second finds arrays without a cap, and the third finds imports of zod/v3. The second line only catches schemas written on a single line, so check multi-line definitions by eye. For an approach to managing API types and validation together, see also From OpenAPI to oRPC.

Scope of verification

On October 13, 2026, we reviewed the colinhacks/zod repository (fix commit #6652, the history of v4.6.5 to v4.6.7, tests, and the api.mdx and compile.mdx docs) and the npm publication dates (document research). We also installed zod 4.6.5, 4.6.6 and 4.6.7 on Linux with Node.js 22.22.0 and compared error counts, element validation counts and processing time with the same script (hands-on testing; times are the median of five runs on one machine). We did not verify behavior in combination with HTTP servers or form libraries, or z.record() and z.tuple(). On October 16, 2026, we reviewed the GitHub release notes (v4.6.6), PR #6652, Issues #6643 and #1872, and public information on CVE-2023-54404 (NVD, VulnCheck, OSV).

For help reviewing input validation in APIs or business systems that use Zod, contact us through Consultation on development, AI, and automation.

References

Share this articleXFacebook
Kakeru Suzuki

Fascinated by the possibilities of technology, has had a deep interest in programming and digital art since student days

Turn this article's theme into your company's next step

Concrete steps forward for your organization.

We organize your desired architecture, legacy systems, and operational requirements to formulate your next steps toward execution.

  • Desired architecture
  • Integration with existing environments
  • Operational requirements
Consult on development & operations initiatives

You can consult with us from the initial conceptual stage. Details from this article will be carried over to the inquiry form.

Receive the latest articles by email