microsoft/aspire · error · RuntimeException
Disconnected from AppHost
Error message
Disconnected from AppHost
What it means
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.
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.
Example fix
// before: reusing a stale transport after disconnect
Object result = transport.invokeCapability("build", args);
// after: recreate the transport when disconnected
try {
Object result = transport.invokeCapability("build", args);
} catch (RuntimeException e) {
if (String.valueOf(e.getMessage()).contains("Disconnected from AppHost")) {
transport = Transport.connect(appHostProcess);
Object result = transport.invokeCapability("build", args);
} else { throw e; }
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before each request batch, check transport liveness if exposed.
if (transport.isDisconnected()) { transport = Transport.connect(appHostProcess); } Try / catch
try {
Object result = transport.invokeCapability(capabilityId, args);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("Disconnected from AppHost")) {
// restart AppHost and rebuild the transport, then retry once
appHost = AppHost.start();
transport = Transport.connect(appHost.getProcess());
Object result = transport.invokeCapability(capabilityId, args);
} else {
throw e;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- -32000
- REMOTE_APP_HOST_SOCKET_PATH environment variable not set…
- -32603
- AppHost is incompatible with the CLI. The AppHost must be…
- AppHost path not found in configuration.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/086222479aad0ee9.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.CodeGeneration.Java/Resources/Transport.java:416
}
});
} finally {
for (String cancellationId : new HashSet<>(cancellationIds)) {
unregisterCancellation(cancellationId);
}
}
}
private Object sendRequest(String method, Object params) {
return sendRequest(method, params, null);
}
private Object sendRequest(String method, Object params, Runnable requestSent) {
CompletableFuture<Object> pendingResponse = new CompletableFuture<>();
int id;
synchronized (connectionStateLock) {
if (disconnected) {
throw disconnectedException();
}
id = requestId.incrementAndGet();
pendingRequests.put(id, pendingResponse);
}
Map<String, Object> request = new HashMap<>();
request.put("jsonrpc", "2.0");
request.put("id", id);
request.put("method", method);
request.put("params", params);
debug("Sending request " + method + " with id=" + id);
try {
ensureReaderLoopStarted();
sendMessage(request);
if (requestSent != null) {View on GitHub (pinned to 25830f84bd)