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
- Update yt-dlp to the latest version (yt-dlp -U or package manager) so it emits valid JSON for the target URL.
- 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.
- Check that yt-dlp is writing JSON to stdout and all diagnostics to stderr; fix any redirection or wrapper that mixes them.
- Verify the URL is reachable/supported (test without credentials, check for age/login restrictions).
- 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
- Keep yt-dlp updated (yt-dlp -U) before discovery runs.
- Probe unknown URLs by running yt-dlp's JSON dump manually to confirm the site is supported.
- Ensure yt-dlp diagnostics go to stderr and only JSON to stdout; don't merge streams.
- Retry once on YtdlpException after confirming the binary version and URL validity.
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)