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

  1. Verify the dashboard API URL/port points at the actual Aspire dashboard (check ASPIRE_DASHBOARD_ env vars and the printed dashboard URL)
  2. Check for proxies/auth redirects returning HTML and bypass or authenticate them
  3. Ensure the dashboard is running a version compatible with the CLI's telemetry API
  4. 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

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


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)