Ctrl+C does not stop the dev server: raw mode suppresses SIGINT and close() never exits the process
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 58/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- node.js, typescript
Direzione di ricerca
Inizia da ShortcutsManager.setup(), #handleKeyPress() e DevServer.close(), quindi riproduci il problema con node ace serve --watch. Traccia la scorciatoia per uscire e la pulizia di watcher/server; il lavoro è completato quando Ctrl+C restituisce il prompt della shell senza lasciare un processo dev-server orfano.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
With node ace serve --watch, pressing Ctrl+C does not stop the dev server. The terminal stays wedged and the only way out is closing the terminal tab, which leaves orphaned node ace serve --watch processes re-parented to init (ppid=1).
Crucially, pressing Ctrl+C repeatedly (quickly or slowly) has no effect either, because in this state no signal is ever generated — so there is no default-terminate fallback to rely on.
Steps to reproduce
node ace serve --watch- Wait for
watching file system for changes... - Press Ctrl+C
Expected: the dev server shuts down and the shell prompt returns.
Actual: ^C is echoed, nothing else happens. The process keeps running. Closing the terminal tab leaves the server running in the background.
Analysis
ShortcutsManager.setup() puts stdin into raw mode so it can implement the r/c/o/h shortcuts:
setup() {
if (!process.stdin.isTTY) return;
process.stdin.setRawMode(true);
this.#keyPressHandler = (data) => this.#handleKeyPress(data.toString());
process.stdin.on("data", this.#keyPressHandler);
}
Raw mode clears ISIG, so the terminal no longer translates Ctrl+C into SIGINT. The keypress is instead delivered as a raw byte and handled explicitly:
#handleKeyPress(key) {
if (key === "\x03" || key === "\x04") return this.#callbacks.onQuit();
...
}
onQuit is () => this.close(), and DevServer.close() is:
async close() {
this.#cleanupKeyboardShortcuts();
await this.#watcher?.close();
if (this.#httpServer) {
this.#httpServer.removeAllListeners();
this.#httpServer.kill("SIGKILL");
}
}
close() never calls process.exit() — it relies on the event loop draining naturally. If anything still holds a referenced handle in the parent process (or close() itself does not settle), the process simply never exits. And because raw mode has suppressed SIGINT, the usual safety net — the OS killing the process on a second Ctrl+C — is gone.
Note removeAllListeners() is called on the child immediately before kill("SIGKILL"), which also removes the exit/close listeners that would normally let the parent observe and reap it.
Not fixed by upgrading
I diffed the published builds: ShortcutsManager.setup, #handleKeyPress and DevServer.close() are byte-identical in 8.0.0 (what we run) and 8.4.0 (current latest), so this is not a regression that a version bump resolves.
Workaround
Making stdin a non-TTY causes setup() to return before enabling raw mode, which restores normal terminal signal handling:
// package.json
"dev": "node ace serve --watch < /dev/null"
Ctrl+C then works as expected. The cost is losing the keyboard shortcuts, so we keep the original as a second script for anyone who wants them.
Worth noting for others hitting this: if your app registers its own SIGINT handler (job runners commonly do, to drain in-flight work), you may need to disable that in development too — otherwise the restored signal is trapped by the child instead.
Suggested fix
Guarantee the process actually exits on the quit shortcut, e.g. await this.close() then process.exit(0) in the onQuit path; or restore raw mode and re-raise SIGINT (process.kill(process.pid, 'SIGINT')) so normal signal semantics apply. Either would also make a second Ctrl+C effective as a fallback.
Environment
@adonisjs/assembler |
8.0.0 (behaviour confirmed unchanged in 8.4.0) |
@adonisjs/core |
7.3.1 |
| Node | v24.14.0 |
| npm | 11.9.0 |
| OS | macOS 26.3 (arm64) |
Happy to test a patch or open a PR if the suggested direction looks right.
- Lingua principale
- TypeScript
- Stelle
- 40
- Fork
- 24
- Merge medio
- 10g 22h
- PR unite (30g)
- 4
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di adonisjs/assembler
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
-
Support of typescript 7Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 66/100
-
OpenAPI schema generationForse di nuovo libera @thetutlage l’ha presa 91 giorni fa e non c’è nessuna pull request aperta. Aperta
Tutte le issue di adonisjs/assembler
Issue simili
-
Link Checker ReportApertaautomated issue report
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
databendlabs/databend-docs#3511 ·
-
area/dashboard kind/bug QA/dev-automation
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
rancher/dashboard#19379 · 2 commenti ·
I maintainer di solito rispondono entro 5 giorni
-
perf(core): getComments() runs the approved count and the comment list as two sequential queriesApertaarea/core bot:bug bot:working
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
emdash-cms/emdash#3905 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
lingdojo/kana-dojo#31728 · 1 commento · 5 reazioni ·
I maintainer di solito rispondono entro 1 giorno
-
selective-claw: freshTailTurns=0 keeps ALL turns verbatim and summarizes none (slice(-0) === slice(0))Forse già presa @zjncs l’ha presa oggi. Apertacomponent:tokenless
Difficoltà 2/5 1-3 ore Idoneità per principianti 80/100
agentic-os-org/ANOLISA#6112 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno