SubtitleEdit/subtitleedit · error · InvalidOperationException

Downloaded yt-dlp ({assetName}) failed SHA-256 verification

Error message

Downloaded yt-dlp ({assetName}) failed SHA-256 verification — expected {expected}, got {actual}. The file has been removed.

What it means

Thrown by VerifyChecksumAsync in YtDlpDownloadService after a yt-dlp download when the computed SHA-256 of the saved file does not match the pinned expected hash in KnownSha256 for that (version, assetName) pair. The corrupted/tampered file is deleted via TryDeleteFile before the throw, so the next attempt re-downloads.

Source

Thrown at src/ui/Logic/Download/YtDlpDownloadService.cs:184

    /// checksum for <paramref name="version"/>. A tampered, truncated, or
    /// otherwise corrupt download is deleted and surfaced as an error instead
    /// of being executed. If no checksum is on record for the asset, this is a
    /// no-op — we don't block on data we don't have.
    /// </summary>
    internal static async Task VerifyChecksumAsync(string filePath, string version, CancellationToken cancellationToken)
    {
        var assetName = Path.GetFileName(filePath);
        if (!KnownSha256.TryGetValue(version, out var byAsset) ||
            !byAsset.TryGetValue(assetName, out var expected))
        {
            return;
        }

        var actual = await ComputeSha256Async(filePath, cancellationToken);
        if (!string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase))
        {
            TryDeleteFile(filePath);
            throw new InvalidOperationException(
                $"Downloaded yt-dlp ({assetName}) failed SHA-256 verification — expected {expected}, got {actual}. The file has been removed.");
        }
    }

    internal static async Task<string> ComputeSha256Async(string filePath, CancellationToken cancellationToken)
    {
        await using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
        var hash = await SHA256.HashDataAsync(stream, cancellationToken);
        return Convert.ToHexString(hash).ToLowerInvariant();
    }

    private static void TryDeleteFile(string filePath)
    {
        try
        {
            if (File.Exists(filePath))
            {
                File.Delete(filePath);

View on GitHub (pinned to 17a9f07487)

Solutions

  1. Retry the download once — transient truncation/network corruption is the most common cause and the bad file is already removed.
  2. If it persists, verify your network path is not MITM'd (compare a manual curl SHA against yt-dlp's published checksum).
  3. Update the KnownSha256 entry to the currently published checksum if upstream re-released the same version.
  4. Confirm Se.DataFolder is writable and not on a filesystem that silently alters bytes (e.g. encoding conversion).

Example fix

// before
await _ytDlp.DownloadYtDlp(progress, ct);

// after: retry once, then surface a clear message
try
{
    await _ytDlp.DownloadYtDlp(progress, ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("SHA-256"))
{
    await _ytDlp.DownloadYtDlp(progress, ct); // bad file was auto-deleted; one retry
}
Defensive patterns

Strategy: retry

Try / catch

try { await svc.DownloadYtDlp(progress, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("SHA-256 verification"))
{ await svc.DownloadYtDlp(progress, ct); } // one retry; bad file was auto-deleted

Prevention

When it happens

Trigger: A completed yt-dlp download whose bytes differ from the pinned checksum: truncated transfer, transparent proxy/MITM, a mirrored/corrupted release, or a yt-dlp version whose KnownSha256 entry was updated to a newer build than what was downloaded.

Common situations: Corporate proxy that rewrites binaries, a flaky connection that truncated the download, or a stale KnownSha256 table after yt-dlp published a re-tagged release with the same version number.

Related errors


AI-assisted analysis of SubtitleEdit/subtitleedit@17a9f07487 (2026-08-13). Data as JSON: /api/errors/a9961670b45644b3. Report an issue: GitHub.