NickvisionApps/Parabolic · error · YtdlpException

Unexpected output format from yt-dlp

Error message

Unexpected output format from yt-dlp: {processResult.Output}

What it means

GetForUrlAsync runs yt-dlp expecting JSON on stdout, then throws YtdlpException when the output cannot be parsed as a JSON object (or is not an object at the root). This indicates yt-dlp emitted something unexpected instead of the anticipated JSON metadata document.

Solutions

  1. Update yt-dlp to the latest version (yt-dlp -U or package manager) so it emits valid JSON for the target URL.
  2. Run the exact yt-dlp dump-json command manually for the URL and inspect the raw stdout/stderr to see what is actually being printed.
  3. Check that yt-dlp is writing JSON to stdout and all diagnostics to stderr; fix any redirection or wrapper that mixes them.
  4. Verify the URL is reachable/supported (test without credentials, check for age/login restrictions).
  5. Catch YtdlpException, log the raw output, and retry with a fresh yt-dlp invocation or a corrected URL.

Example fix

// before
var result = await discoveryService.GetForUrlAsync(url); // yt-dlp outdated, prints error text
// after
RunYtdlpUpdateCheck(); // ensure yt-dlp is current before invoking
try
{
    var result = await discoveryService.GetForUrlAsync(url);
}
catch (YtdlpException ex)
{
    _logger.LogError(ex, "yt-dlp returned non-JSON output for {Url}; run yt-dlp -U and verify the URL", url);
}
Defensive patterns

Strategy: try-catch

Validate before calling

static bool LooksLikeJson(string s)
{
    s = s.TrimStart();
    return s.StartsWith("{") || s.StartsWith("[");
}
// check processResult.Output with LooksLikeJson before/after calling GetForUrlAsync

Type guard

static bool IsJsonObjectOutput(string output)
{
    try { using var doc = JsonDocument.Parse(output); return doc.RootElement.ValueKind == JsonValueKind.Object; }
    catch (JsonException) { return false; }
}

Try / catch

try { var result = await service.GetForUrlAsync(url); }
catch (YtdlpException ex) { _logger.LogError(ex, "yt-dlp output was not JSON for {Url}; update yt-dlp and check URL support", url); }

Prevention

When it happens

Trigger: Calling GetForUrlAsync(url) when yt-dlp writes warnings, progress text, an error message, or malformed data to stdout so JsonDocument.Parse fails or the root element is not a JSON object. Also occurs with an outdated/broken yt-dlp build or a wrapper script that prints extra output.

Common situations: Outdated yt-dlp version broken by a site change printing error text instead of JSON; antivirus/shell wrappers injecting output; URL requiring authentication so yt-dlp returns an error message; stderr redirected into stdout; disk-full or locale issues corrupting the output.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.


AI-assisted analysis of NickvisionApps/Parabolic@1118e6a3ab (2026-09-15). Data as JSON: /api/errors/1bb6021d2052b632. Report an issue: GitHub.

Appendix: source

Thrown at Nickvision.Parabolic.Shared/Services/DiscoveryService.cs:78

    }

    public Task<DiscoveryResult> GetForUrlAsync(Uri url, Credential? credential = null, CancellationToken cancellationToken = default) => GetForUrlAsync(url, credential, string.Empty, string.Empty, cancellationToken);

    private async Task<DiscoveryResult> GetForUrlAsync(Uri url, Credential? credential, string suggestedSaveFolder, string suggestedFilename, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation($"Discovering media for {url}...");
        var processResult = await _ytdlpExecutableService.ExecuteAsync(_ytdlpExecutableService.GetDiscoveryProcessArguments(url, credential), cancellationToken);
        if (processResult.ExitCode != 0 && (string.IsNullOrEmpty(processResult.Output) || processResult.Output[0] != '{'))
        {
            _logger.LogError($"Failed to discover media for {url}: {processResult.Error.TrimEnd()}");
            throw new YtdlpException(processResult.Error);
        }
        cancellationToken.ThrowIfCancellationRequested();
        using var json = JsonDocument.Parse(processResult.Output);
        if (json.RootElement.ValueKind != JsonValueKind.Object)
        {
            _logger.LogError($"Unexpected output format from yt-dlp for {url}: {processResult.Output.TrimEnd()}");
            throw new YtdlpException($"Unexpected output format from yt-dlp: {processResult.Output}");
        }
        DiscoveryResult? result = null;
        if (json!.RootElement.TryGetProperty("entries", out var entriesProperty) && entriesProperty.GetArrayLength() > 0)
        {
            var urlInfos = new List<DiscoveryResult>();
            foreach (var entry in entriesProperty.EnumerateArray())
            {
                cancellationToken.ThrowIfCancellationRequested();
                if (entry.ValueKind != JsonValueKind.Object)
                {
                    continue;
                }
                if (entry.TryGetProperty("ie_key", out var ieProperty) && (ieProperty.GetString() ?? string.Empty) == "YoutubeTab" && entry.TryGetProperty("url", out var urlProperty))
                {
                    var urlInfo = await GetForUrlAsync(new Uri(urlProperty.GetString() ?? string.Empty), credential, suggestedSaveFolder, suggestedFilename, cancellationToken);
                    if (urlInfo is not null)
                    {
                        urlInfos.Add(urlInfo);

View on GitHub (pinned to 1118e6a3ab)