microsoft/aspire · error · HttpRequestException
Dashboard API returned unexpected content type
Error message
Dashboard API returned unexpected content type '{0}'. Expected JSON response. What it means
TelemetryCommandHelpers.EnsureTelemetryApiResponse validates that the Dashboard API's HTTP response is both successful and JSON-typed before deserialization. When the response content type is not JSON, it throws HttpRequestException (HttpRequestError.InvalidResponse) formatted with the actual media type, guarding downstream JSON parsing from garbage.
Solutions
- Verify the dashboard API URL/port points at the actual Aspire dashboard (check ASPIRE_DASHBOARD_ env vars and the printed dashboard URL)
- Check for proxies/auth redirects returning HTML and bypass or authenticate them
- Ensure the dashboard is running a version compatible with the CLI's telemetry API
- Catch HttpRequestException and inspect the message for the actual media type received
Example fix
// before
var url = "http://localhost:18888"; // some other service
// after
var url = Environment.GetEnvironmentVariable("ASPIRE_DASHBOARD_URL") ?? "http://localhost:18888"; Defensive patterns
Strategy: try-catch
Validate before calling
if (!response.IsSuccessStatusCode ||
!response.Content.Headers.ContentType?.MediaType!.Contains("json")!) { /* bail before parsing */ } Type guard
static bool IsJsonResponse(HttpResponseMessage r) =>
r.Content.Headers.ContentType?.MediaType is string mt &&
(mt.Contains("json") || mt.EndsWith("+json")); Try / catch
try { await telemetry.GetAllResourcesAsync(ct); }
catch (HttpRequestException ex) when (ex.Message.Contains("content type"))
{ Console.Error.WriteLine("Dashboard returned non-JSON; check the dashboard URL/port."); } Prevention
- Verify the dashboard URL/port before calling the API
- Confirm you're talking to the Aspire dashboard, not a proxy or another service
- Check dashboard and CLI version compatibility
- Handle auth/login flows so HTML error pages aren't returned
When it happens
Trigger: Calling telemetry/resource list commands (e.g. GetAllResourcesAsync) against a dashboard endpoint that returns HTML (error page, auth redirect), plain text, or an empty body with no content type.
Common situations: Wrong dashboard URL/port configured (ASPIRE_DASHBOARD_* env vars); dashboard behind a proxy or auth page returning HTML; dashboard version serving a different endpoint shape; hitting a plain HTTP server instead of the dashboard API.
Related errors
- Dashboard did not become ready within the expected time.
- Expected a JSON array.
- HTTP command request content type cannot be specified…
- Import is disabled.
- The request body was larger than the max allowed of
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/f84ca15202a28686.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Commands/TelemetryCommandHelpers.cs:166
/// </exception>
public static void EnsureTelemetryApiResponse(HttpResponseMessage response)
{
// A 200 with text/html content type indicates the Blazor fallback route handled the request,
// meaning the telemetry API endpoint doesn't exist. Treat this the same as a 404.
if (response.IsSuccessStatusCode &&
response.Content.Headers.ContentType?.MediaType is "text/html")
{
throw new HttpRequestException(
HttpRequestError.InvalidResponse,
statusCode: HttpStatusCode.NotFound);
}
response.EnsureSuccessStatusCode();
if (!HasJsonContentType(response))
{
var mediaType = response.Content.Headers.ContentType?.MediaType ?? "(none)";
throw new HttpRequestException(
HttpRequestError.InvalidResponse,
string.Format(CultureInfo.InvariantCulture, TelemetryCommandStrings.UnexpectedContentType, mediaType),
inner: null,
response.StatusCode);
}
}
/// <summary>
/// Resolves an AppHost connection and gets Dashboard API info.
/// </summary>
/// <param name="connectionResolver">The connection resolver for AppHost discovery.</param>
/// <param name="interactionService">The interaction service for displaying messages.</param>
/// <param name="httpClientFactory">The HTTP client factory for making API calls.</param>
/// <param name="logger">The logger for diagnostic messages.</param>
/// <param name="projectFile">The optional AppHost project file.</param>
/// <param name="dashboardUrl">The optional direct dashboard URL (mutually exclusive with <paramref name="projectFile"/>).</param>
/// <param name="apiKey">The optional API key for dashboard authentication.</param>
/// <param name="requireDashboard">View on GitHub (pinned to 25830f84bd)