microsoft/aspire · error · DistributedApplicationException

Failed to parse JSON output into type

Error message

Failed to parse JSON output into type '{typeName}':
{output}

What it means

CallCliAsJsonAsync captures the devtunnel CLI's stdout and deserializes it into type T. If the output is not valid JSON (JsonException), the client logs the raw output and rethrows it wrapped in this DistributedApplicationException.

Solutions

  1. Update the devtunnel CLI to the latest version so JSON output matches what Aspire expects
  2. Read the raw output in the message to see exactly what the CLI returned
  3. Verify the command succeeds when run manually (`devtunnel list --json`) and compare output
  4. Check whether another tool/alias named `devtunnel` is shadowing the real CLI on PATH

Example fix

// before
// devtunnel CLI v0.1 prints text banners -> JsonException
// after
// update CLI
devtunnel update  # or reinstall latest from https://learn.microsoft.com/azure/developer/dev-tunnels
// then retry the app host
Defensive patterns

Strategy: validation

Validate before calling

var psi = new ProcessStartInfo("devtunnel", "--version") { RedirectStandardOutput = true };
using var p = Process.Start(psi)!;
var version = (await p.StandardOutput.ReadToEndAsync()).Trim();
if (Version.TryParse(version.Split(' ')[^1].TrimStart('v'), out var v) && v < new Version(1, 0))
    throw new InvalidOperationException($"devtunnel CLI too old: {version}");

Type guard

static bool LooksLikeJson(string s) => s.TrimStart().StartsWith('{') || s.TrimStart().StartsWith('[');

Try / catch

try { await client.ListTunnelsAsync(logger, ct); }
catch (DistributedApplicationException ex) when (ex.Message.StartsWith("Failed to parse JSON"))
{ logger.LogError(ex, "devtunnel CLI returned non-JSON output; update the CLI"); throw; }

Prevention

When it happens

Trigger: The devtunnel CLI prints human-readable text, warnings, or login prompts to stdout instead of JSON; an old CLI version emits a different output shape; the command fails before producing JSON.

Common situations: Outdated devtunnel CLI without JSON output support for a flag; CLI emitting deprecation/telemetry banners on stdout; truncation or interleaved stderr/stdout in redirected streams.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/0bd49ca379fe9e5d. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.DevTunnels/DevTunnelCliClient.cs:339

            logger?.LogError("CLI call returned empty output with exit code '{ExitCode}'.", exitCode);
            return (default, exitCode, "CLI call returned empty output.");
        }

        try
        {
            if (!string.IsNullOrEmpty(propertyName))
            {
                output = JsonDocument.Parse(output).RootElement.GetProperty(propertyName).GetRawText();
                logger?.LogTrace("Extracted JSON property '{PropertyName}':\n{Output}", propertyName, output);
            }
            var result = JsonSerializer.Deserialize<T>(output, _jsonOptions);
            logger?.LogTrace("JSON output successfully deserialized to '{TypeName}' instance", typeof(T).Name);
            return (result, 0, default);
        }
        catch (JsonException ex)
        {
            logger?.LogError(ex, "Failed to parse JSON output into type '{TypeName}':\n{Output}", typeof(T).Name, output);
            throw new DistributedApplicationException($"Failed to parse JSON output into type '{typeof(T).Name}':\n{output}", ex);
        }
    }

    private record DevTunnelDeleteResult(string DeletedTunnel);
}

View on GitHub (pinned to 25830f84bd)