Skip to content

Commit 31878f4

Browse files
authored
fix(cli): guard systeminformation.cpu() against uncatchable os.cpus() throw on ARM Linux (hotplug skew) — see #1374
### Patch refined and tested The patch in commit `1333802` is close, but the `cpus()` pre-check needs a fix: on Bun's ARM Linux runtime with CPU hotplug skew, `os.cpus()` can throw synchronously, so calling `cpus()` without a `try/catch` can crash before we ever reach the guarded `si.cpu()`. Updated patch: - `cpus()` wrapped in `try/catch` — if it throws, return empty values + `logger.warn` immediately. - `systeminformation.cpu()` in `try/catch` — catches the uncaught exception from the `nextTick` callback inside `lib/cpu.js:956`. - `Promise.all` now uses `getCpuInfoSafe()` (removed the duplicate `systeminformationModule.cpu()` call). - Fallback logging via the existing `logger` (`fingerprintType: cpu_pre_check_failed` / `cpu_info_failed`). Diagnosis confirmed via the mount-namespace workaround: - frozen CPU snapshot (8 CPUs consistent across /proc/stat, /proc/cpuinfo, /sys/devices/system/cpu/online) → CLI stays up. - live skewed state (9/8/9) → crash ~3s after start. Fallback behavior: fingerprint still generates (empty CPU fields or legacy path); process is stable, not ideal for fingerprinting uniqueness but no longer crashes.
1 parent e0ca374 commit 31878f4

1 file changed

Lines changed: 44 additions & 1 deletion

File tree

cli/src/utils/fingerprint.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,49 @@ async function getMachineId(): Promise<string> {
3333
return id
3434
}
3535

36+
async function getCpuInfoSafe(): Promise<{
37+
manufacturer: string
38+
brand: string
39+
cores: number
40+
physicalCores: number
41+
}> {
42+
try {
43+
// Bun's os.cpus() can throw synchronously on ARM Linux with CPU hotplug
44+
// skew (/proc vs /sys disagreement). Probe first; if it throws, skip
45+
// si.cpu() entirely and fall back to empty values so the process stays up.
46+
// See #1374.
47+
cpus()
48+
} catch {
49+
logger.warn(
50+
{ fingerprintType: 'cpu_pre_check_failed' },
51+
'os.cpus() unavailable, skipping CPU fingerprinting — see #1374',
52+
)
53+
return { manufacturer: '', brand: '', cores: 0, physicalCores: 0 }
54+
}
55+
56+
try {
57+
if (!systeminformationModule) {
58+
systeminformationModule = await import('systeminformation')
59+
}
60+
const info = await systeminformationModule.cpu()
61+
return {
62+
manufacturer: info.manufacturer,
63+
brand: info.brand,
64+
cores: info.cores,
65+
physicalCores: info.physicalCores,
66+
}
67+
} catch (err) {
68+
logger.warn(
69+
{
70+
fingerprintType: 'cpu_info_failed',
71+
error: err instanceof Error ? err.message : String(err),
72+
},
73+
'systeminformation.cpu() failed, using empty CPU fingerprint — see #1374',
74+
)
75+
return { manufacturer: '', brand: '', cores: 0, physicalCores: 0 }
76+
}
77+
}
78+
3679
async function getSystemInfo(): Promise<{
3780
system: { manufacturer: string; model: string; serial: string; uuid: string }
3881
cpu: { manufacturer: string; brand: string; cores: number; physicalCores: number }
@@ -44,7 +87,7 @@ async function getSystemInfo(): Promise<{
4487
}
4588
const [systemInfo, cpuInfo, osInfo] = await Promise.all([
4689
systeminformationModule.system(),
47-
systeminformationModule.cpu(),
90+
getCpuInfoSafe(),
4891
systeminformationModule.osInfo(),
4992
])
5093
return {

0 commit comments

Comments
 (0)