60s startup delay in ConPTY: DA1 sentinel never receives response
Summary
Claude Code takes ~60 seconds to start when running inside a ConPTY pseudo-terminal (embedded terminals, VS Code integrated terminal on Windows, terminal multiplexers). Running directly in PowerShell starts in ~2 seconds.
Root Cause
Claude's Ink renderer sends an XTVERSION query (ESC[>0q) followed by a DA1 sentinel (ESC[c) during startup:
// From InternalApp (WJ8) when raw mode is first enabled:
setImmediate(() => {
Promise.all([this.querier.send(WU7()), this.querier.flush()]).then(([K]) => {
if (K) Rp7(K.name), k(`XTVERSION: terminal identified as "${K.name}"`);
else k("XTVERSION: no reply (terminal ignored query)");
});
});
The querier has no explicit timeout — it relies entirely on the DA1 response as a sentinel:
flush() {
return new Promise((A) => {
this.sentinels.push(A);
this.stdout.write(ZO9); // ESC[c (DA1)
});
}
Problem: ConPTY swallows the DA1 query (ESC[c]) — it intercepts it internally but does NOT send a response back to the child process's stdin. The XTVERSION query (ESC[>0q) IS passed through to the output pipe, but DA1 is not. Since the DA1 response never arrives, Promise.all never resolves, and Claude blocks for ~60 seconds until some internal Node.js event loop fallback kicks in.
Evidence
Timestamped PTY output from crew-board's embedded terminal (ConPTY, flags=0):
+0.0s read#1 160 bytes (ConPTY screen clear)
+0.9s read#2 11 bytes (title: "claude")
+1.3s read#3 27 bytes (ESC[?2004h ESC[?1004h ESC[>1u ESC[?25l)
+1.4s read#4 5 bytes (ESC[>0q — XTVERSION query appears in output)
NOTE: ESC[c (DA1) does NOT appear — ConPTY swallowed it
+63.0s read#5 1030 bytes (Claude logo renders — 61.6 seconds later!)
ESC[>0qappears in the output pipe (ConPTY passes it through)ESC[cdoes NOT appear (ConPTY intercepts it internally)- Host cannot send DA1 response through ConPTY's input pipe (ConPTY processes VT on its input pipe rather than forwarding to the child)
- Result: 60-second startup delay every time
Comparison: pwsh starts instantly in the same ConPTY setup. claude --version via cmd /c takes 454ms. The delay is specifically in Claude's interactive terminal initialization.
Suggested Fix
Add an explicit timeout (2-3 seconds) to the querier's flush() method:
flush() {
return new Promise((resolve) => {
this.sentinels.push(resolve);
this.stdout.write(ZO9); // ESC[c
// Timeout: if DA1 response doesn't arrive in 3s, resolve anyway
setTimeout(() => {
const idx = this.sentinels.indexOf(resolve);
if (idx !== -1) {
this.sentinels.splice(idx, 1);
// Resolve all pending queries as undefined (no response)
for (const pending of this.pending.splice(0)) pending.resolve(undefined);
resolve();
}
}, 3000);
});
}
This would reduce the startup delay from ~60s to ~3s in ConPTY environments while maintaining the DA1 sentinel behavior for real terminals.
Environment
- Windows 11, native PowerShell (not WSL)
- Claude Code 2.1.79 (npm install)
- ConPTY via portable-pty (Rust), flags=0
- Node.js v22 via nvm4w
- Also affects: VS Code integrated terminal (#24584), any ConPTY host
Related Issues
- #24584 — Native binary freezes in VS Code terminal (same ConPTY issue)
- #23211 — Windows Terminal input frozen (ConPTY ANSI processing)
- #14352 — WSL2 slow startup (different root cause but similar symptoms)
Labels
bug, windows, performance
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗