π Search Terms
SIGINT, SIGTERM, ctrl-c, ctrl+c, signal, interrupt, tsgo does not exit,
native tsc kill, exit code 0 killed, cancel build
π Version & Regression Information
- This changed between versions 6.x (the JavaScript compiler) and 7.0 (the native compiler).
Running the TS 6 tsc.js under Node and sending SIGTERM mid-compile terminates it
immediately; the native tsc in 7.0.2 ignores it and runs to completion.
- Also reproduces on
typescript@7.1.0-dev.20260908.1 (nightly), so it is not already fixed.
- Tested on macOS 15 (Darwin 25.5.0), arm64,
@typescript/typescript-darwin-arm64.
β― Playground Link
No response
π» Code
Not reproducible in the Playground β this is CLI/process behaviour. Any project large enough
to leave a signalling window will do. This generator produces one using nothing but plain
interfaces and functions (no errors, no deep generics, no external libraries):
// gen.js
const fs = require('fs');
fs.mkdirSync('src', { recursive: true });
for (let f = 0; f < 30000; f++) {
let s = '';
for (let i = 0; i < 20; i++) {
s += `export interface I${f}_${i} { id: string; n: number; ok: boolean; tags: string[]; at: Date; }\n`;
s += `export function f${f}_${i}(x: I${f}_${i}): I${f}_${i} { return { ...x, n: x.n + 1 }; }\n`;
s += `export const c${f}_${i} = f${f}_${i}({ id: "a", n: ${i}, ok: true, tags: ["t"], at: new Date() });\n`;
}
if (f > 0) s += `import { f${f - 1}_0 } from './m${f - 1}';\n`;
fs.writeFileSync(`src/m${f}.ts`, s);
}
fs.writeFileSync('tsconfig.json', JSON.stringify({
compilerOptions: { strict: true, target: 'ES2020', module: 'commonjs', noEmit: true, skipLibCheck: true },
include: ['src'],
}, null, 2));
node gen.js
npm i typescript@7
TSC=node_modules/@typescript/typescript-<platform>/lib/tsc
$TSC -p tsconfig.json --extendedDiagnostics > out.txt 2>&1 &
PID=$!
sleep 10
ps -p $PID # confirm it is genuinely still compiling
kill -INT $PID # or -TERM
wait $PID; echo "exit=$?"
grep 'Total time' out.txt # if this prints, the compile finished anyway
Confirming the process is still alive before signalling matters β a build that finishes
first gives a false negative.
π Actual behavior
SIGINT and SIGTERM sent to a compile in progress are ignored completely. The compiler
finishes the whole program, prints its full diagnostics, and exits 0, as though no signal
had been sent. Ctrl-C therefore cannot interrupt a build.
Signal sent 10 s into a compile of 30 000 files / 1.9 M lines / 0 errors:
| Signal |
Exit code |
Outcome |
SIGINT |
0 |
Ignored. Full diagnostics printed β compile ran to completion. |
SIGTERM |
0 |
Ignored. Full diagnostics printed β compile ran to completion. |
SIGHUP |
129 |
Terminates ~0.3 s after the signal. No diagnostics. |
SIGINT, idle --watch |
0 |
Exits within ~1 s, as expected. |
"Ran to completion" is not inferred from wall-clock timing β the interrupted runs emit the
full Files / Lines / Check time / Total time diagnostics block, which only a finished
compile prints.
Two controls show the signals are genuinely reaching the process and that a handler exists:
SIGHUP kills a running compile instantly, and SIGINT works normally in --watch mode
while idle. SIGINT/SIGTERM appear to be trapped and then not acted upon while compiling.
π Expected behavior
SIGINT and SIGTERM should stop the compile promptly and exit non-zero β conventionally
130 and 143. This is what the TypeScript 6 JavaScript compiler does.
Additional information about the issue
Two practical consequences beyond Ctrl-C being unresponsive:
-
A cancelled build reports success. Exiting 0 after SIGTERM means any supervisor that
terminates tsc β a CI job timeout, a cancelled workflow, a build runner stopping a task β
records the build as having passed.
-
Build orchestrators leak compiler processes. A tool that builds several packages in
parallel and stops at the first failure signals the remaining ones with SIGTERM and exits.
Because they ignore it, they are reparented to init/launchd and keep running β
uninterruptible and invisible β each holding gigabytes of heap. In our monorepo a single
failing service left 16 compilers running and exhausted system memory. pnpm 12 works around
it by escalating to SIGKILL; pnpm 11 does not, and neither does Ctrl-C.
π Search Terms
SIGINT,SIGTERM,ctrl-c,ctrl+c,signal,interrupt,tsgo does not exit,native tsc kill,exit code 0 killed,cancel buildπ Version & Regression Information
Running the TS 6
tsc.jsunder Node and sendingSIGTERMmid-compile terminates itimmediately; the native
tscin 7.0.2 ignores it and runs to completion.typescript@7.1.0-dev.20260908.1(nightly), so it is not already fixed.@typescript/typescript-darwin-arm64.β― Playground Link
No response
π» Code
Not reproducible in the Playground β this is CLI/process behaviour. Any project large enough
to leave a signalling window will do. This generator produces one using nothing but plain
interfaces and functions (no errors, no deep generics, no external libraries):
Confirming the process is still alive before signalling matters β a build that finishes
first gives a false negative.
π Actual behavior
SIGINTandSIGTERMsent to a compile in progress are ignored completely. The compilerfinishes the whole program, prints its full diagnostics, and exits 0, as though no signal
had been sent. Ctrl-C therefore cannot interrupt a build.
Signal sent 10 s into a compile of 30 000 files / 1.9 M lines / 0 errors:
SIGINTSIGTERMSIGHUPSIGINT, idle--watch"Ran to completion" is not inferred from wall-clock timing β the interrupted runs emit the
full
Files / Lines / Check time / Total timediagnostics block, which only a finishedcompile prints.
Two controls show the signals are genuinely reaching the process and that a handler exists:
SIGHUPkills a running compile instantly, andSIGINTworks normally in--watchmodewhile idle.
SIGINT/SIGTERMappear to be trapped and then not acted upon while compiling.π Expected behavior
SIGINTandSIGTERMshould stop the compile promptly and exit non-zero β conventionally130 and 143. This is what the TypeScript 6 JavaScript compiler does.
Additional information about the issue
Two practical consequences beyond Ctrl-C being unresponsive:
A cancelled build reports success. Exiting 0 after
SIGTERMmeans any supervisor thatterminates
tscβ a CI job timeout, a cancelled workflow, a build runner stopping a task βrecords the build as having passed.
Build orchestrators leak compiler processes. A tool that builds several packages in
parallel and stops at the first failure signals the remaining ones with
SIGTERMand exits.Because they ignore it, they are reparented to
init/launchdand keep running βuninterruptible and invisible β each holding gigabytes of heap. In our monorepo a single
failing service left 16 compilers running and exhausted system memory. pnpm 12 works around
it by escalating to
SIGKILL; pnpm 11 does not, and neither does Ctrl-C.