CS193V dev environment installer hangs at “Installing Claude Code…” (step 16/26)
Summary: during the container build, /home/student/.local/bin/claude --version (the Claude Code 2.1.225 native binary, built on Bun v1.4.0) spins at 100% CPU forever instead of printing a version. It has been running for 1 h 43 min with no syscalls and no network activity.
Probable cause: the VM’s virtual CPU (“Common KVM processor”) only exposes the x86-64-v1 baseline: no SSE4.2, no POPCNT, no AVX/AVX2. Even Bun’s baseline x64 build needs SSE4.2 + POPCNT. The installer doesn’t check CPU instruction sets, and the build step has no timeout, so the build hangs silently instead of failing with a clear message.
1. What the student sees
The installer gets through the podman install, the file download and the first 15 build steps, then stays on this screen with the spinner still moving:
Building the course container
This may take a few minutes.
localhost/cs193v:local
⡿ [████████████████████████░░░░░░░░░░░░░░░░] 16/26
Installing Claude Code...
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ added 2 packages in 6s ┃
┃ npm warn allow-scripts 1 package has install scripts not yet cover… ┃
┃ npm warn allow-scripts @anthropic-ai/claude-code@2.1.225 (postin… ┃
┃ npm warn allow-scripts ┃
┃ npm warn allow-scripts Run `npm approve-scripts --allow-scripts-pe… ┃
┃ + su student -s /bin/sh -c npm cache clean --force ┃
┃ npm warn using --force Recommended protections disabled. ┃
┃ + /home/student/.local/bin/claude --version ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
The spinner never stops, so from the student’s side it looks like a slow step, not a failure. Nothing on screen hints that it will never finish.
2. Environment
| Host OS | Ubuntu 24.04.5 LTS, kernel 6.8.0-45-generic x86_64 |
|---|---|
| Virtualization | KVM (full), systemd-detect-virt = kvm |
| CPU model | “Common KVM processor” (family 15, model 6, stepping 1), 2 vCPUs |
| x86-64 level (per glibc ld.so) | v1 only — v2 / v3 / v4 all not supported |
| Memory / disk | 3.8 GiB RAM (3.2 GiB available) · 14 GB free on / |
| Podman | 4.9.3 (go1.22.2), rootless, cgroup v2 (systemd), crun 1.14.1, slirp4netns networking |
| Installer | curl -fsSL cs193v.stanford.edu/cs193v-dev/files/install-cs193v.sh | bash → release-0.0.2 |
| Container base | Ubuntu 26.04 LTS (glibc) |
| Claude Code | @anthropic-ai/claude-code 2.1.225 → claude-code-linux-x64 (glibc variant, correct for the base) |
| Binary | bin/claude.exe, 297,831,432 bytes, ELF x86-64, not stripped BuildID 433066516d42fe55315fdc10a614b61c2836d8f6 sha256 0a3be8d18cb0f5357d38ce2d588601753a60b44cc9c622579ed8b8405dee231e embedded runtime: Bun v1.4.0 (contains string bun-linux-x64-baseline) |
CPU flags exposed to the VM
fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx
fxsr sse sse2 ht syscall nx lm constant_tsc nopl xtopology cpuid tsc_known_freq pni
cx16 x2apic hypervisor lahf_lm cpuid_fault pti
| sse4_2 | missing |
|---|---|
| popcnt | missing |
| avx / avx2 / avx512f | missing |
| bmi1 / bmi2 / fma / lzcnt / movbe | missing |
| sse / sse2 / pni (sse3) / cx16 / lahf_lm | present |
3. Evidence that it is a spin, not a slow download
PID PPID STAT STARTED (UTC) ELAPSED %CPU WCHAN COMMAND
43622 43436 Sl+ 2026-09-26 05:16:24 01:44:44 1.6 futex_wait_queue podman build ... -t localhost/cs193v:local
60900 43622 Sl+ 2026-09-26 05:19:31 01:41:37 0.3 futex_wait_queue \_ buildah-oci-runtime
60911 60900 Ss 2026-09-26 05:19:31 01:41:37 0.0 do_wait \_ /bin/sh -c set -eux; su student ... npm install -g ...; /home/student/.local/bin/claude --version
61320 60911 Rl 2026-09-26 05:19:38 01:41:30 99.8 - \_ /home/student/.local/bin/claude --version
60913 43622 S 2026-09-26 05:19:31 01:41:37 0.0 do_poll \_ slirp4netns ...
- User-space CPU only: utime 6,098.9 s vs. stime 3.0 s. Kernel reports the task as
running(no blocking syscall). - Almost never yields: 20 voluntary vs. 164,956 involuntary context switches.
- No network: no sockets in the container’s network namespace (
ss -tanpempty). Open fds are just stdio pipes, an epoll fd, an eventfd,/dev/urandomand/proc/40/statm(its own memory stats). - Small footprint: 2 threads (main +
mi-scavenger, the mimalloc thread Bun uses), RSS 29 MB, virtual 9.8 GB (normal for a Bun process). - Tight loop: a 5-second
perfsample at 199 Hz puts about 99.9% of samples on a dozen addresses in two small ranges:
14.99% 0x3910081 8.85% 0x1c6750d 8.85% 0x3910050
8.15% 0x1c674d2 7.95% 0x1c674fa 7.95% 0x3910088
7.85% 0x3b96544 7.34% 0x1c674bb 7.04% 0x1c674b0
7.04% 0x3910056 7.04% 0x391007a 6.84% 0x3910064
These are non-PIE virtual addresses in the binary above (BuildID given), so they can be symbolized against it. The binary isn’t stripped. There was no gdb/addr2line on this box, and I didn’t want to attach a debugger to the live process.
4. Analysis
- It’s not a wrong-binary problem.
bin/claude.exeis a hard link (same inode) tonode_modules/@anthropic-ai/claude-code-linux-x64/claude, the glibc x64 build, which matches the Ubuntu 26.04 base. Thenpm warn allow-scripts … postinstalllines appear, but the link thatinstall.cjscreates is there, so the warning doesn’t seem to be the problem. - It’s not networking or podman. Steps 1–15 ran fine in the same build, including other native binaries. At step 15 the Codex binary (a musl static build) ran
--versionwithout trouble. - The CPU is below what the runtime targets. Bun’s standard x64 build targets Haswell-class CPUs (AVX2). Its baseline build targets Nehalem-class CPUs, which still need SSE4.2 and POPCNT. This vCPU has neither, so it’s below both targets. On CPUs that are too old, Bun normally crashes with
SIGILL. Here it spins silently instead, which may be the runtime’s fallback or feature-detection path looping when the expected CPU features are missing. That last step is a hypothesis; the missing CPU features are measured. - The build has no timeout, so one hung
--versionblocks the whole install indefinitely, and the progress UI keeps animating.
5. Timeline (UTC, 2026-09-26)
| 05:15:57 | installer started (course-install.sh … release-0.0.2); podman + uidmap installed; course files fetched |
|---|---|
| 05:16:24 | cs193v --rebuild → podman build started |
| 05:19:31 | STEP 16/25 (npm install -g @anthropic-ai/claude-code@2.1.225) started |
| 05:19:38 | claude --version started → began spinning |
| 05:51 | (unrelated) Docker installed on the host for a separate service. This was 32 minutes after the hang began, so it isn’t the cause. |
| 07:01 | data collected; process still at 99.8% CPU, state R |
6. Reproduction
# On any x86-64 VM whose vCPU lacks SSE4.2/POPCNT (e.g. QEMU/KVM with a
# generic CPU model such as qemu64/kvm64 instead of "host"):
curl -fsSL cs193v.stanford.edu/cs193v-dev/files/install-cs193v.sh | bash
# Quick pre-check on the machine:
grep -o -w -E 'sse4_2|popcnt|avx2' /proc/cpuinfo | sort -u
/lib64/ld-linux-x86-64.so.2 --help | grep x86-64-v # look for "(supported, searched)"
Faster isolated repro (untested here, since I didn’t want to disturb the running build): in the same image, run timeout 30 /home/student/.local/bin/claude --version; echo $?. On an affected CPU it should exit 124 (timed out) instead of printing 2.1.225.
7. Suggested fixes for the installer
- Check the CPU up front, in “Looking at your computer”: on x86_64, require
sse4_2andpopcntin/proc/cpuinfo(x86-64-v2). If they’re missing, fail with a plain message, e.g. “Your CPU (or your VM’s virtual CPU) is too old for Claude Code. If this is a VM, set its CPU type to host or at least x86-64-v2.” - Time-box the version checks in the Containerfile:
timeout 120 /home/student/.local/bin/claude --version(and the same for Codex etc.), so a hang becomes a build failure with a log line instead of an endless spinner. - Add a stall detector to the progress meter: if a step’s output hasn’t changed for N minutes, say so and point to the log file (
/tmp/cs193v-build-*.log). - Minor inconsistency: step 16 runs
/home/student/.local/bin/claude --versionas root (HOME=/root), while step 15 runs the Codex check viasu student. This probably isn’t related to the hang, but it’s worth making consistent.
8. Workaround for affected students
Change the VM’s CPU model to host (or x86-64-v2/newer), reboot, then rerun the installer. Physical machines from roughly 2009 onward (Intel Nehalem / AMD Bulldozer and later) aren’t affected.
Appendix: build log tail (/tmp/cs193v-build-8cb2c627ad3e.log, 156,842 bytes, last write 05:19)
+ su student -s /bin/sh -c npm install -g '@openai/codex@0.148.0'
added 2 packages in 8s
+ su student -s /bin/sh -c npm cache clean --force
npm warn using --force Recommended protections disabled.
+ su student -s /bin/sh -c /home/student/.local/bin/codex --version
+ grep -qF 0.148.0
+ CODEX_BIN=/home/student/.local/lib/node_modules/@openai/codex/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/bin/codex
+ uname -m
+ WANT_ELF=3e00
+ test 3e00 = 3e00
--> bb34351f5ea6
STEP 15/25: ARG CLAUDE_CODE_VERSION=2.1.225
--> c58008dc73f7
STEP 16/25: RUN set -eux; su student -s /bin/sh -c "npm install -g '@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}'"; su student -s /bin/sh -c 'npm cache clean --force'; /home/student/.local/bin/claude --version
+ su student -s /bin/sh -c npm install -g '@anthropic-ai/claude-code@2.1.225'
added 2 packages in 6s
npm warn allow-scripts 1 package has install scripts not yet covered by allowScripts:
npm warn allow-scripts @anthropic-ai/claude-code@2.1.225 (postinstall: node install.cjs)
npm warn allow-scripts
npm warn allow-scripts Run `npm approve-scripts --allow-scripts-pending` to review, or `npm approve-scripts <pkg>` to allow.
+ su student -s /bin/sh -c npm cache clean --force
npm warn using --force Recommended protections disabled.
+ /home/student/.local/bin/claude --version
The meter reports 16/26 while podman reports 16/25. That’s expected: the meter adds one step for container creation.
Appendix: /proc/61320/status (selected)
Name: claude
State: R (running)
NSpid: 61320 40
Threads: 2
VmPeak: 9808104 kB
VmRSS: 29524 kB
Cpus_allowed_list: 0-1
voluntary_ctxt_switches: 20
nonvoluntary_ctxt_switches: 164956
utime_ticks=609889 stime_ticks=297 (CLK_TCK=100)