{"record":{"id":"d3f55a445293a2ca","repo":"karatelabs/karate","slug":"interrupted-while-shutting-down-http-server","errorCode":null,"errorMessage":"Interrupted while shutting down HTTP server","messagePattern":"Interrupted while shutting down HTTP server","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/http/HttpServer.java","lineNumber":167,"sourceCode":"\n    /** Graceful, blocking, idempotent — {@link #stopAndWait()} by another name, for\n     *  {@link KarateLifecycle}. Servers that wrap one of these (mock server, OAuth2 callback\n     *  server) inherit registration from here and must not register themselves as well. */\n    @Override\n    public void stop() {\n        stopAndWait();\n    }\n\n    public void stopAndWait() {\n        stopAsync();\n        try {\n            // the closeFuture, not group termination, is what guarantees the port is free on return\n            channel.closeFuture().sync();\n            bossGroup.terminationFuture().sync();\n            workerGroup.terminationFuture().sync();\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new RuntimeException(\"Interrupted while shutting down HTTP server\", e);\n        }\n        logger.debug(\"stop: shutdown complete\");\n    }\n\n    public void stopAsync() {\n        KarateLifecycle.unregister(this);\n        logger.debug(\"stop: shutting down\");\n        // close the listen socket first — group termination closes registered channels only as a\n        // side effect, and (without a quiet period) can complete before the fd is released\n        channel.close();\n        // no quiet period: a server being torn down accepts nothing more, so waiting for one to\n        // pass only delays the (blocking) stop — the timeout still bounds in-flight work\n        bossGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS);\n        workerGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS);\n    }\n\n    private HttpServer(String host, int requestedPort, SslContext sslContext, Function<HttpRequest, HttpResponse> handler, SseHandler sseHandler, WsHandler wsHandler) {\n        this.handler = handler;","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/http/HttpServer.java#L149-L185","documentation":"HttpServer.stopAndWait() shuts down the Netty boss/worker event loops and waits on their termination futures. If the waiting thread is interrupted, the method restores the interrupt flag and rethrows this RuntimeException wrapping the InterruptedException, since a silent partial shutdown could leave the port bound.","triggerScenarios":"The thread calling server.stop() (or stopAndWait()) is interrupted while blocked on channel close/termination futures — e.g. test framework or executor shutdown interrupts the thread.","commonSituations":"JUnit timeout rules or TestNG timeouts interrupting a hung stop; application shutdown hooks cancelled mid-stop; a test failing elsewhere and the runner tearing down with interrupt.","solutions":["Ensure stop() is called on a thread that will not be interrupted, or finish shutdown in a dedicated non-interrupted thread.","Remove overly aggressive timeouts (JUnit @Timeout, executor shutdownNow) around server stop.","Retry/complete the shutdown in a fresh thread; the server close is idempotent per channel closeFuture semantics.","Investigate why the interrupt occurred — usually a stuck earlier phase of the test.","­"],"exampleFix":"// before\nsomeExecutor.shutdownNow(); // interrupts in-flight stop()\n// after\nsomeExecutor.shutdown();\nsomeExecutor.awaitTermination(30, TimeUnit.SECONDS);","handlingStrategy":"try-catch","validationCode":"if (!Thread.currentThread().isInterrupted()) server.stopAndWait();","typeGuard":null,"tryCatchPattern":"try { server.stopAndWait(); } catch (RuntimeException e) { if (e.getCause() instanceof InterruptedException) { /* retry or log; interrupt already re-set */ } else throw e; }","preventionTips":["Call stop() from a thread not subject to interruption/timeouts","Avoid shutdownNow() on executors that may still be stopping the server","Investigate recurring interrupts — they usually indicate a hung earlier phase"],"tags":["netty","server-shutdown","thread-interrupt"],"backgroundTag":"thread-interrupted","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}