[Bug]: `shopify hydrogen dev` exits before listening when the npm registry is unreachable (p-cancelable onCancel-after-settle in latest-version chunk)
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 48/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- node.js, typescript
- Lĩnh vực
- cli
Hướng nghiên cứu
Bắt đầu từ getLatestNPMPackageVersion trong dist/chunk-GW4JHUIA.js và lần theo caller checkForNewVersionOnCLI của nó trong dist/chunk-IF6EYTHG.js, bao gồm cả đường dẫn Hydrogen trong dist/chunk-IF6EYTHG.js. Tái hiện với quyền truy cập npm-registry bị chặn; được xem là hoàn tất khi shopify hydrogen dev tiếp tục chạy và đạt đến output lắng nghe cục bộ mà không có lỗi p-cancelable không được bắt.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Please confirm that you have:
- Searched existing issues to see if your issue is a duplicate.
- Reproduced the issue in the latest CLI version.
In which of these areas are you experiencing a problem?
Hydrogen custom storefront
Expected behavior
shopify hydrogen dev starts the dev server. The startup "is there a newer version?" check is a
courtesy; if the npm registry is unreachable it should degrade to a debug log and the server should
still come up.
Actual behavior
When the npm registry is unreachable, shopify hydrogen dev exits 1 before the dev server ever
listens. No ➜ Local: line is printed. The process dies with p-cancelable's internal invariant:
> shopify hydrogen dev --codegen --port "$PORT"
The `envFile` option is deprecated, please use `envDir: false` instead.
[vite] (ssr) Re-optimizing dependencies because vite config has changed
[vite] (client) Re-optimizing dependencies because vite config has changed
Error: The `onCancel` handler was attached after the promise settled.
at o (node_modules/@shopify/cli/dist/latest-version-LDWU5F7A.js:11:17603)
at d (node_modules/@shopify/cli/dist/latest-version-LDWU5F7A.js:19:47843)
at t.<anonymous> (node_modules/@shopify/cli/dist/latest-version-LDWU5F7A.js:19:49069)
at t.wrapper (node:events:639:12)
at t.emit (node:events:514:20)
at t.emit (node:domain:473:12)
at node_modules/@shopify/cli/dist/latest-version-LDWU5F7A.js:19:37968
at runNextTicks (node:internal/process/task_queues:65:5)
at processTimers (node:internal/timers:615:9)
This is a hard startup failure, not a degraded notification.
Reproduction steps
- Scaffold or use any Hydrogen storefront;
npm ciwith network sonode_modulesis complete. - Remove npm-registry reachability. Any of these reproduce it: firewall
DROP, firewall
REJECT/ECONNREFUSED, or refusing DNS. npm run dev(i.e.shopify hydrogen dev).
Exits 1 with the trace above. Reproduced on Node 26.8.2 / npm 11.19.1, Debian trixie (node:26-slim),
linux/arm64 and linux/amd64.
Analysis
From the shipped bundle of 4.7.1:
latest-version-*.js is dynamically imported from exactly one place — getLatestNPMPackageVersion
in dist/chunk-GW4JHUIA.js:
async function ne(e) { // getLatestNPMPackageVersion
return d(s`Getting the latest version of NPM package: ${c.raw(e)}`),
y("cmd_all_timing_network_ms")(async () => {
const { default: n } = await import("./latest-version-LDWU5F7A.js");
return n(e);
});
}
whose only caller is checkForNewVersionOnCLI, which already wraps it defensively:
async function Pe(e, n, { cacheExpiryInHours: a = 0 } = {}) { // checkForNewVersionOnCLI
const t = async () => (d(s`Checking if there's a version of ${e} newer than ${n}`), ne(e));
let p;
try { p = await O(`npm-package-${e}`, t, a * 3600 * 1000); } catch { return; }
if (p && new SemVer(n).compare(p) < 0) return p;
}
The try/catch cannot help. The thrown error is p-cancelable's guard in its own constructor:
const o = a => {
if (!this._isPending) throw new Error("The `onCancel` handler was attached after the promise settled.");
this._cancelHandlers.push(a);
};
and the stack bottoms out in processTimers → runNextTicks, reaching node:domain. So the throw
originates from a timer callback firing after the awaited promise already settled — outside the
await, therefore outside the catch. It escapes as an uncaught exception and terminates the
process. latest-version-*.js contains 22 setTimeout and 1 setInterval.
checkForNewVersionOnCLI has two callers in the bundle: the auto-upgrade path
(dist/chunk-4FI2YBRF.js) and Hydrogen's own version check in dist/chunk-IF6EYTHG.js. The latter
is the one that reaches hydrogen dev, and its only early-outs are a module-level flag and
next/experimental/snapshot version strings.
Notably not workarounds
Each measured, not assumed:
CI=1— no effect. The CI check inversionToAutoUpgradesits after the lookup and governs a
different function; the Hydrogen-side caller has no CI guard at all.- Refusal vs. black-holing — no effect. With
nftables reject with tcp resetreturning
ECONNREFUSEDin 3 ms (kernel counters confirmed non-zero after the run), the crash is
byte-identical to a silentdrop. Settle timing is not the variable; a stale timer is. - Upgrading the CLI — no effect.
dist/latest-version-LDWU5F7A.jsis byte-identical
(sha256c493bb882df5b786…) across 4.6.1, 4.7.1, 4.8.0, and the 2026-09-14 nightly. - Env opt-out — none exists. All 42
SHOPIFY_CLI_*variables in the bundle were inventoried;
SHOPIFY_CLI_FORCE_AUTO_UPGRADEonly forces the upgrade, andautoUpgradeEnabledlives in the
conf store rather than the environment.
Impact
Any environment that runs a Hydrogen dev server without npm-registry egress cannot start the
storefront at all: CI sandboxes, offline development, and restricted-egress hosting. In our case the
dev server runs under a deliberately locked-down egress policy, so this is unconditional.
Suggested fix
Make the version check unable to reject into the process. Either attach the onCancel handler
synchronously during construction (before any await), or defensively guard the registration:
if (promise.isPending) promise.onCancel(...)
and clear the pending timers when the request settles or aborts. An explicit opt-out
(SHOPIFY_CLI_SKIP_VERSION_CHECK=1) would also let restricted environments avoid the network call
entirely.
Operating System
Debian GNU/Linux 13 (trixie), containerized; reproduced on both arm64 and amd64.
Shopify CLI version
4.7.1 (chunk identical in 4.6.1, 4.8.0, nightly)
Node version
26.8.2
- Ngôn ngữ chính
- TypeScript
- Star
- 750
- Fork
- 293
- Merge trung bình
- 3 ngày 11 giờ
- Pull request đã merge (30 ngày)
- 107
Chuẩn bị môi trường
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của Shopify/cli
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
Shopify/cli#8388 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 42/100
Shopify/cli#8386 · 26 bình luận · 33 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 15/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 15/100
Maintainer thường phản hồi trong vòng 1 ngày
Issue tương tự
-
bug
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
StabilityNexus/Fate-EVM-Frontend#153 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
code-yeongyu/oh-my-openagent#9039 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
Tencent/teamai-cli#862 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug good first issue hacktoberfest redis
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
libredb/libredb-studio#1164 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
flake
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
coder/xum#4920 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày