Skip to content

fix(cli): load undici after the Node version preflight - #1207

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-self-driving/fixwizard-load-undici-after-the-node-7f4b66
Draft

fix(cli): load undici after the Node version preflight#1207
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-self-driving/fixwizard-load-undici-after-the-node-7f4b66

Conversation

@posthog

@posthog posthog Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Users on Node below 22.10 who run npx @posthog/wizard get a raw TypeError: webidl.util.markAsUncloneable is not a function stack instead of the friendly upgrade message. The wizard is the onboarding entry point, so each one is a person who cannot finish setup and is not told why. First seen shortly after 2.72.0 reached npm, with no occurrences in the preceding two weeks.
  • bin.ts imported undici statically. ESM evaluates every static import before the first statement of the module, so undici loaded before the satisfies(process.version, '>=22.22.0') preflight could run. The preflight comment says it exists to stop exactly this crash, but import hoisting made it unreachable.
  • Deps stay external in tsdown.config.ts, so npx installs undici 8.5.0 next to whatever Node the user has.

Changes

  • Move undici out of the static import graph. It now loads through await import('undici') after the preflight passes, so the version guard is genuinely the first thing that runs. The setGlobalDispatcher(new Agent({ allowH2: false })) workaround and its TODO(#1198) context are unchanged, just relocated.
  • Delete a stale comment that claimed the guard had to sit above the other imports. Import hoisting means statement order never gave it that property.
  • Add smoke-test check 2: fail the build if a static undici import reappears in dist/bin.js. CI runs one modern Node, so this is the check that survives the next dependency bump.
Node Before After
20.19.0 TypeError: webidl.util.markAsUncloneable is not a function upgrade message, PHW_CLI_NODE_VERSION, exit 1
24.19.0 normal run normal run

Test plan

Built and ran the compiled binary on both runtimes.

Node 20.19.0, before and after

Before:

TypeError: webidl.util.markAsUncloneable is not a function
    at new CacheStorage (.../undici/lib/web/cache/cachestorage.js:20:17)
    at Object.<anonymous> (.../undici/index.js:179:25)

After:

The PostHog wizard needs a newer version of Node.js to run.

  You have:  v20.19.0
  You need:  v22.22.0 or later
...
phw-error: {"code":"PHW_CLI_NODE_VERSION","message":"Node v20.19.0 is below the required range >=22.22.0"}

Exit code 1.

  • Node 24.19.0: node dist/bin.js --help prints the command list as before.
  • pnpm build (runs the smoke test and the warlock smoke test) passes.
  • New smoke check verified both ways: it fails when a static undici import is spliced into dist/bin.js, and passes on the real build.
  • npx vitest run: 2601 tests pass, 161 files.
  • pnpm lint: 0 errors. pnpm typecheck: 27 errors before and after this change, none in bin.ts.

LLM context

Written by a PostHog self-driving agent.

  • Reproduced the crash first on a downloaded Node 20.19.0 build, then confirmed the fix against the same binary. Unit tests alone would not have caught this, since the crash happens at module load in the published bundle.
  • Checked whether the rest of the import graph also breaks on Node 20 by building a probe entry with only the undici lines removed. It does not, so the narrow fix is enough and bin.ts did not need splitting into a preflight shim plus a lazily imported main module. That split would make the guard structurally first for every dependency, not just undici, and is the deeper fix if another dep ever crashes on load.
  • The new smoke check is undici-specific on purpose. Most dependencies load fine on old Node, so a general "nothing is statically imported" rule would not hold.
  • Searched open PRs and issues for this crash and found none in flight.

Created with PostHog Desktop from this inbox report.

A static `import ... from 'undici'` in bin.ts runs before any statement in the
entry module, so undici loaded before the Node version check could run. On Node
below 22.10 that load throws `webidl.util.markAsUncloneable is not a function`,
and users saw that stack instead of the upgrade message.

Load undici with a dynamic import after the preflight passes. Add a smoke-test
check that fails the build if undici returns to the static import graph.

Generated-By: PostHog Desktop
Task-Id: 29ffcd2b-0ca5-48c6-a645-5172d0e37c9c
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci ai-observability
  • /wizard-ci basic-integration
  • /wizard-ci mcp-analytics
  • /wizard-ci replay-vision
  • /wizard-ci revenue
  • /wizard-ci self-driving
  • /wizard-ci warehouse
  • /wizard-ci warehouse-seeded

Test an individual app:

  • /wizard-ci ai-observability/anthropic
  • /wizard-ci ai-observability/google-adk
  • /wizard-ci ai-observability/groq
Show more apps
  • /wizard-ci ai-observability/manual-capture
  • /wizard-ci ai-observability/openai
  • /wizard-ci ai-observability/openai-agents
  • /wizard-ci ai-observability/opentelemetry
  • /wizard-ci ai-observability/vercel-ai
  • /wizard-ci basic-integration/android
  • /wizard-ci basic-integration/angular
  • /wizard-ci basic-integration/astro
  • /wizard-ci basic-integration/django
  • /wizard-ci basic-integration/fastapi
  • /wizard-ci basic-integration/flask
  • /wizard-ci basic-integration/flutter
  • /wizard-ci basic-integration/javascript-node
  • /wizard-ci basic-integration/javascript-web
  • /wizard-ci basic-integration/laravel
  • /wizard-ci basic-integration/next-js
  • /wizard-ci basic-integration/nuxt
  • /wizard-ci basic-integration/python
  • /wizard-ci basic-integration/rails
  • /wizard-ci basic-integration/react-native
  • /wizard-ci basic-integration/react-router
  • /wizard-ci basic-integration/sveltekit
  • /wizard-ci basic-integration/swift
  • /wizard-ci basic-integration/tanstack-router
  • /wizard-ci basic-integration/tanstack-start
  • /wizard-ci basic-integration/vue
  • /wizard-ci mcp-analytics/custom-dispatcher
  • /wizard-ci mcp-analytics/typescript-sdk
  • /wizard-ci replay-vision/javascript-node
  • /wizard-ci replay-vision/next-js
  • /wizard-ci replay-vision/react-vite
  • /wizard-ci revenue/stripe
  • /wizard-ci self-driving/astro
  • /wizard-ci self-driving/fastapi
  • /wizard-ci self-driving/nuxt
  • /wizard-ci self-driving/react-router
  • /wizard-ci self-driving/sveltekit
  • /wizard-ci warehouse/monorepo-env
  • /wizard-ci warehouse/multi-source-next
  • /wizard-ci warehouse/stripe-node
  • /wizard-ci warehouse/zero-source
  • /wizard-ci warehouse-seeded/next-stripe
  • /wizard-ci warehouse-seeded/next-stripe-declined

Test against a Context Mill branch:

  • /wizard-ci all context-mill:my-branch

Add context-mill:<branch> to any command above to pin the Context Mill branch. It defaults to main.

Results will be posted here when complete.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants