Skip to content

fix(core): Widen zod peer dependency range in published packages (no-changelog)#29376

Merged
garritfra merged 1 commit intomasterfrom
node-zod-peer-range-and-guard
Apr 29, 2026
Merged

fix(core): Widen zod peer dependency range in published packages (no-changelog)#29376
garritfra merged 1 commit intomasterfrom
node-zod-peer-range-and-guard

Conversation

@garritfra
Copy link
Copy Markdown
Contributor

Summary

Replace the exact catalog: (zod@3.25.67) peer-dependency pin in the published packages n8n-workflow, n8n-core, and @n8n/api-types with a real semver range: ">=3.25.0 <4".

Why

PR #28604 correctly moved zod to peerDependencies in these three packages to enforce a single zod instance across the workspace. The peer was declared as "catalog:", which gets published as the exact catalog version (3.25.67).

That exact pin is what we want for our own builds, but it is not how package managers act on a peer:

  • pnpm/npm/yarn treat an exact version as a single point. Consumers whose graph also pulls in an older zod (e.g. 3.24.x via another transitive) silently satisfy the peer and only emit a warning.
  • Our published code does import … from 'zod/v4' (the subpath was added in zod 3.25.0), so installs that resolve to an older zod fail at runtime with a cryptic Package subpath './v4' is not defined by "exports" deep inside require-in-the-middle. We've been seeing this in production via Sentry alerts on creators-nodes-sync.

Widening the peer to ">=3.25.0 <4":

  • Communicates the actual runtime contract (anything from 3.25.0 up to but not including 4.0.0).
  • Lets pnpm install --strict-peer-dependencies fail on a too-old zod instead of just warning.
  • Makes the warning text package managers emit actually actionable.

Downstream remediation

Consumers seeing the cryptic error today (e.g. creators-nodes-sync) should pin zod via package.json:

"pnpm": {
  "overrides": {
    "zod": "3.25.67"
  }
}

…or the equivalent overrides (npm) / resolutions (yarn) idiom, then reinstall.

What this PR is not

An earlier draft of this PR also added a load-time runtime guard inside n8n-workflow to throw a clearer error if the consumer's resolved zod was too old. Self-review uncovered that the guard:

  1. broke the n8n-workflow CJS build (resolveJsonModule: false is set in @n8n/typescript-config/modern/tsconfig.cjs.json, so import 'zod/package.json' did not compile), and
  2. would, even if compiled, fail at runtime in the ESM build on Node ≥ 22 because emitted JSON imports require with { type: 'json' } import attributes.

It was reverted. The peer-range change alone is the smaller, safer win. We can revisit a runtime guard later (likely via createRequire + filesystem read, with a built-artifact integration test) if needed.

Related Linear tickets, Github issues, and Community forum posts

Sentry alert: creators-nodes-sync — issue 7440848191 (Module.patchedRequire(require-in-the-middle:index)Package subpath './v4' is not defined by "exports" in n8n-workflow@2.18.3_zod@3.24.2).

Follows up on #28604.

Review / Merge checklist

  • I have seen this code, I have run this code, and I take responsibility for this code.
  • PR title and summary are descriptive.
  • Docs updated or follow-up ticket created.
  • Tests included. (N/A — peer-dep range string change; no logic change. pnpm test, pnpm lint, pnpm typecheck, and pnpm build in packages/workflow are all clean. pnpm install produces no lockfile diff.)
  • PR Labeled with Backport to Beta, Backport to Stable, or Backport to v1 (if the PR is an urgent fix that needs to be backported)

Made with Cursor

…changelog)

Replace the exact `catalog:` (3.25.67) zod peer pin in `n8n-workflow`,
`n8n-core`, and `@n8n/api-types` with a real semver range (`>=3.25.0 <4`).

The exact pin was correct for our internal usage but is not what package
managers act on when resolving a peer dependency: an exact version is
treated as a single point, so consumers whose other dependencies pull in
an older zod (e.g. 3.24.x) silently satisfy the peer with a warning
instead of an actionable error. Source code in `n8n-workflow` imports
`zod/v4`, a subpath that was only added in zod 3.25.0, so installs that
land on an older zod fail at runtime with a cryptic
`Package subpath './v4' is not defined by "exports"` deep inside
`require-in-the-middle`.

Widening the peer range to `>=3.25.0 <4` makes the install-time signal
meaningful for downstream consumers (and lets `pnpm install
--strict-peer-dependencies` fail rather than warn).

Made-with: Cursor
@garritfra garritfra requested a review from lucamattiazzi April 28, 2026 09:25
@garritfra garritfra marked this pull request as ready for review April 28, 2026 09:25
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 3 files

Architecture diagram
sequenceDiagram
    participant Dev as Consumer (Dev/CI)
    participant PM as Package Manager (pnpm/npm)
    participant App as Node.js Process
    participant Core as n8n Packages (workflow/core)
    participant Zod as zod (Dependency)

    Note over PM, Core: Dependency Resolution Phase

    Dev->>PM: Run installation (pnpm install)
    PM->>Core: Read package.json peerDependencies
    
    alt zod version < 3.25.0
        PM-->>Dev: CHANGED: Emit actionable warning or hard failure (strict-peer-dependencies)
        Note right of PM: Previously exact-version pin caused cryptic silent failures
    else zod version >= 3.25.0 < 4.0.0
        PM-->>Dev: Resolution successful
    end

    Note over App, Zod: Runtime Execution Phase

    Dev->>App: Execute application (e.g. n8n start)
    App->>Core: Load module
    
    Core->>Zod: CHANGED: Resolve 'zod/v4' subpath
    
    alt Resolved zod < 3.25.0
        Zod-->>Core: THROW: "Package subpath './v4' is not defined"
        Core-->>App: Runtime Crash (Sentry Alert)
    else Resolved zod >= 3.25.0
        Note over Zod: Subpath './v4' available in exports
        Zod-->>Core: Module loaded successfully
        Core-->>App: Execution continues
    end

    Note over Dev, Zod: Downstream remediation via overrides/resolutions ensures valid range matches runtime contract
Loading

@n8n-assistant n8n-assistant Bot added core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team labels Apr 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Performance Comparison

Comparing currentlatest master14-day baseline

Memory consumption baseline with starter plan resources

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
memory-heap-used-baseline 118.98 MB 118.98 MB 115.72 MB (σ 2.02) +0.0% +2.8% ⚠️
memory-rss-baseline 293.85 MB 293.85 MB 291.38 MB (σ 29.40) +0.0% +0.8%

Idle baseline with Instance AI module loaded

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
instance-ai-heap-used-baseline 191.65 MB 191.65 MB 187.79 MB (σ 2.07) +0.0% +2.1% ⚠️
instance-ai-rss-baseline 354.18 MB 354.18 MB 364.79 MB (σ 21.08) +0.0% -2.9%

docker-stats

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
docker-image-size-runners 413.00 MB 388.00 MB 391.15 MB (σ 9.26) +6.4% +5.6% 🔴
docker-image-size-n8n 1280.00 MB 1300.48 MB 1303.55 MB (σ 51.48) -1.6% -1.8%
How to read this table
  • Current: This PR's value (or latest master if PR perf tests haven't run)
  • Latest Master: Most recent nightly master measurement
  • Baseline: Rolling 14-day average from master
  • vs Master: PR impact (current vs latest master)
  • vs Baseline: Drift from baseline (current vs rolling avg)
  • Status: ✅ within 1σ | ⚠️ 1-2σ | 🔴 >2σ regression

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Bundle Report

Bundle size has no change ✅

@garritfra garritfra added this pull request to the merge queue Apr 29, 2026
Merged via the queue into master with commit ca5320a Apr 29, 2026
57 checks passed
@garritfra garritfra deleted the node-zod-peer-range-and-guard branch April 29, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants