{"record":{"id":"086222479aad0ee9","repo":"microsoft/aspire","slug":"disconnected-from-apphost","errorCode":null,"errorMessage":"Disconnected from AppHost","messagePattern":"Disconnected from AppHost","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java","lineNumber":416,"sourceCode":"                }\n            });\n        } finally {\n            for (String cancellationId : new HashSet<>(cancellationIds)) {\n                unregisterCancellation(cancellationId);\n            }\n        }\n    }\n\n    private Object sendRequest(String method, Object params) {\n        return sendRequest(method, params, null);\n    }\n\n    private Object sendRequest(String method, Object params, Runnable requestSent) {\n        CompletableFuture<Object> pendingResponse = new CompletableFuture<>();\n        int id;\n        synchronized (connectionStateLock) {\n            if (disconnected) {\n                throw disconnectedException();\n            }\n\n            id = requestId.incrementAndGet();\n            pendingRequests.put(id, pendingResponse);\n        }\n\n        Map<String, Object> request = new HashMap<>();\n        request.put(\"jsonrpc\", \"2.0\");\n        request.put(\"id\", id);\n        request.put(\"method\", method);\n        request.put(\"params\", params);\n\n        debug(\"Sending request \" + method + \" with id=\" + id);\n\n        try {\n            ensureReaderLoopStarted();\n            sendMessage(request);\n            if (requestSent != null) {","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java#L398-L434","documentation":"The Aspire Java code-generation transport (JSON-RPC over stdio to the AppHost) throws when a request is attempted after the connection has already been marked disconnected. sendRequest checks the disconnected flag under connectionStateLock and throws the disconnected exception ('Disconnected from AppHost') rather than queueing a request that could never be answered.","triggerScenarios":"invokeCapability or any sendRequest call is made after a previous I/O failure or EOF triggered handleDisconnect() — e.g. the AppHost process exited, the stdio pipe broke, or a prior send threw IOException and marked the transport disconnected.","commonSituations":"AppHost crashed or was stopped while the generated Java client was still issuing capability invocations; long-running session outliving the AppHost; an earlier request failed with IOException and subsequent calls now hit the disconnected guard; not restarting/reconnecting the transport after a disconnect.","solutions":["Check the AppHost process/logs to see why it exited (crash, dashboard shutdown, unhandled exception) and restart it.","Recreate or reconnect the transport/session after a disconnect instead of reusing the stale client — the disconnected flag is terminal for that instance.","Inspect the earlier 'Failed to send request ...' or reader-loop exception that triggered handleDisconnect to find the root cause (broken pipe, EOF on stdin/stdout).","Guard long-lived clients with connection-state checks or retry logic that rebuilds the transport on 'Disconnected from AppHost' errors."],"exampleFix":"// before: reusing a stale transport after disconnect\nObject result = transport.invokeCapability(\"build\", args);\n// after: recreate the transport when disconnected\ntry {\n    Object result = transport.invokeCapability(\"build\", args);\n} catch (RuntimeException e) {\n    if (String.valueOf(e.getMessage()).contains(\"Disconnected from AppHost\")) {\n        transport = Transport.connect(appHostProcess);\n        Object result = transport.invokeCapability(\"build\", args);\n    } else { throw e; }\n}","handlingStrategy":"try-catch","validationCode":"// Before each request batch, check transport liveness if exposed.\nif (transport.isDisconnected()) { transport = Transport.connect(appHostProcess); }","typeGuard":null,"tryCatchPattern":"try {\n    Object result = transport.invokeCapability(capabilityId, args);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Disconnected from AppHost\")) {\n        // restart AppHost and rebuild the transport, then retry once\n        appHost = AppHost.start();\n        transport = Transport.connect(appHost.getProcess());\n        Object result = transport.invokeCapability(capabilityId, args);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Monitor the AppHost process and restart the transport whenever it exits.","Treat 'Disconnected from AppHost' as terminal for the current transport instance; never reuse it.","Log the root IOException/EOF that triggered handleDisconnect to catch broken pipes early.","Keep request sessions shorter than the AppHost lifetime, or implement automatic reconnect."],"tags":["jsonrpc","java","ipc","apphost","disconnected"],"backgroundTag":"connection-refused","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}