Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

shadcn-sync 的 registry 请求没有超时:连接被黑洞时 pnpm shadcn:check 会一直挂着(46 次串行请求) #4031

Closed

Description

@os-zhuang

Migrated from objectstack-ai/objectstack#5968 under the file-at-destination ruling (objectstack-ai/objectstack#7167, maintainer 2026-08-10). Originally filed 2026-08-06T13:27:09Z. The full prior thread — including triage rulings and hold/restart conditions — remains on the source issue and MUST be read before acting on this card.


观察类发现,来自 objectstack#5803 的实现过程(objectui PR objectstack-ai/objectstack#3496),不在该 issue 范围内,未在本 PR 中修。

现象

objectui/scripts/shadcn-sync.js 的 fetchUrl 用 https.get 发起请求,只监听 error 与 end,没有设置任何超时(既没有 req.setTimeout,也没有 socket/连接超时)。

  • 连接被丢包黑洞掉(SYN 无响应),或 TCP 已建立但服务端永不返回,请求就一直挂着。
  • --check 是 46 次串行请求,任何一次挂住,整条命令就停在那里。
  • .github/workflows/shadcn-check.yml 里跑的正是这条命令,挂住会一路耗到 job 超时才失败,日志上看不出原因。

PR objectstack-ai/objectstack#3496 之后,基于响应的失败(非 2xx、非 JSON)都会立刻 reject 并带上原因;没有响应是这条取数路径上剩下的唯一一种静默失败。

现状影响

今天没人踩到:沙箱/CI 的 egress 拦截是立刻返回 403,不是黑洞。因此打 finding 标签、不进 pm:queue,由 PM 定级。

修的话需要决定的事

  1. 超时值(单次请求 10s?整体预算?)。
  2. 超时后是重试一次还是直接算 fetch 失败(现在的错误路径已经能优雅承接:--check 标 error 且退出码不变,--update 拒绝写文件)。

req.setTimeout(ms, () => req.destroy(new Error(...))) 一行即可,但值和重试策略是产品决定,所以单独开。

备注

按 objectstack#4949 的纪律先搜了开放 issue(shadcn-sync timeout、shadcn registry fetch timeout hang)均 0 命中;更宽的 shadcn-sync 查询因共享账号触发 GitHub API 限流没能跑完,如与既有 issue 重复请直接关掉本条。

Activity

  1. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Contributor

    Context from #3586 / PR #4174, which bounds the blast radius of this without fixing it.

    That PR adds timeout-minutes: 20 to the shadcn-check job, so a black-holed connection now costs 20 minutes of a runner instead of the 360-minute default. The fetchUrl gap this issue names is exactly why that line was needed and why it was sized the way it was: with no socket timeout in the script, nothing else bounds the hang.

    Two measurements taken while sizing it, in case they are useful here:

    • Run 31374857502 (2026-08-10) completed all 46 serial requests in ~1.7 s (09:30:22.71 to 09:30:24.03), i.e. ~37 ms per request, with Registry: 0 cached, 46 fetched and 0 errors. So the healthy path has enormous headroom — a per-request timeout of even 10 s would be ~270x the observed per-request cost and would never fire in normal operation.
    • All 30 runs in the workflow's API history completed in 19-49 s wall clock, so there is no historical run that a per-request timeout in that range would have disturbed.

    The job timeout is a backstop, not a substitute: it turns a hang into a 20-minute red job rather than into a fast, well-attributed error naming the component that hung, and it does nothing for anyone running pnpm shadcn:check locally. This issue stays the real fix.


    Generated by Claude Code

  2. self-assigned this
    on Aug 15, 2026
  3. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    Contributor

    Claiming — PM dispatch, with a delegated adjudication (veto window open). Session: session_01RnQd8iMMUwXQEV1crFmQiQ (objectui whole-repo PM seat). Branch: claude/issue-4031-shadcn-sync-timeout.

    Adjudicated (within delegation gates — build-script tooling, no contract surface): per-request timeout 10s, no retry, fail fast with an error that names the URL that hung. Basis: the 2026-08-10 measurements on this card — healthy path is ~37ms/request (10s ≈ 270× headroom), all 30 historical runs complete in 19–49s wall clock, so the timeout never fires in normal operation; a black-holed connection does not resolve in seconds, so a retry only doubles the worst-case hang budget while the existing error path already degrades gracefully (--check marks the error, --update refuses to write). The 20-minute job backstop from PR #4174 stays as-is — this is the real fix it was standing in for. Maintainer may veto/adjust the value on this card; the dispatch proceeds meanwhile.

    Scope: scripts/shadcn-sync.js fetchUrl (req.setTimeout(10_000, …destroy…) shape), all 46 serial requests covered; error text names the component/URL; verify both --check and --update degrade exactly as the card describes. PR will be Fixes #4031.


    Generated by Claude Code

  4. yinlianghui commented on Aug 15, 2026

    @yinlianghui
    Contributor

    <!-- os-dev-report -->

    {
      "issue": 4031,
      "status": "done",
      "branch": "claude/issue-4031-shadcn-sync-timeout",
      "pr": "https://github.com/objectstack-ai/objectui/pull/4715",
      "premise_still_valid": true,
      "summary": "Added a 10s per-request timeout (no retry, per the delegated adjudication on the issue thread) to fetchUrl() in scripts/shadcn-sync.js via req.setTimeout(timeoutMs, () => req.destroy(new Error(...))); the error names the URL that hung and reuses the existing req.on('error', reject) path, so --check/--update degrade exactly as before. timeoutMs threads through fetchRegistry too, defaulting to the same 10s. Mechanism assumptions confirmed: (1) fetchUrl used bare https.get with only error/end listeners, no timeout at all -- true, read at scripts/shadcn-sync.js:139 pre-fix. (2) --check runs 46 serial requests where non-2xx/non-JSON already reject with a reason (PR objectui#3496), so a silent hang was the only remaining failure mode -- true. (3) req.setTimeout + destroy(error) covers both a dropped-SYN and a connected-but-silent peer, since Node arms the idle timer as soon as a socket is assigned to the request, before the handshake resolves -- confirmed: a deterministic get-double is committed as the automated test, and a REAL raw-net hung server was verified manually (standalone Node script, inside a worker_threads Worker, and end-to-end via the real CLI against a temporarily-patched packages/components/shadcn-components.json entry, reverted before commit). The identical real-socket scenario proved flaky specifically inside Vitest's threads pool while this shared container had a sibling agent's heavy pnpm build pegging multiple cores (silent for a 20s it() bound, not slow -- reproduced with -t isolating the one test and --maxWorkers=1), which is a Vitest/contention interaction, not a defect in fetchUrl -- so that path is committed as the double rather than a real-socket vitest test, per this repo's own flaky-test discipline against widening budgets to paper over a race. Full reasoning and the manual verification transcript are in the PR body.",
      "tests": "pnpm exec vitest run scripts/__tests__/shadcn-sync-fetch-cache.test.ts --maxWorkers=2 -> 26/26 passed, 766ms, at HEAD 995a6fdad. pnpm run type-check:scripts (tsc -p tsconfig.scripts.json) -> 0 errors. node scripts/check-control-bytes.mjs -> OK, 4238 tracked files scanned. pnpm run lint:root -> 0 errors, 22 pre-existing warnings, none in changed files. node scripts/check-changeset-presence.mjs -> no changeset owed (scripts/ is not in .changeset/config.json's fixed release group); objectui has no skip-changeset label so none applied -- PR was auto-labeled 'tests' by existing repo automation, read back after creation via pull_request_read. Healthy-path regression check: pnpm shadcn:check against the live (egress-blocked) registry -> all 46 requests fail via immediate HTTP 403, total wall 0.481s, zero timeouts triggered. Manual hang simulation (adjudication route item 2): temporarily pointed the accordion manifest entry at a local raw-net server that accepts a connection and never responds; node scripts/shadcn-sync.js --check -> accordion times out at exactly 10000ms naming the URL (other 45 components unaffected via their normal 403 path), exit 0; node scripts/shadcn-sync.js --update accordion -> same 10000ms timeout naming the URL, md5sum of packages/components/src/ui/accordion.tsx unchanged before/after, git status clean (no write) -- --check/--update both degrade exactly per the existing error path. Manifest reverted via git checkout before the commit; git status/diff --stat confirm only the two intended files changed. Changed files (2): scripts/shadcn-sync.js, scripts/__tests__/shadcn-sync-fetch-cache.test.ts. PR #4715, head sha 995a6fdadb0a6d8e2a2c5a664ce003d9c6bb6a43 (short 995a6fdad) -- gate union above and the PR body both quote this same sha, run after the final commit.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions