Kareadita/Kavita · error · KavitaException

No download URL available for {filePath}

Error message

No download URL available for {filePath}

What it means

Thrown by CblGithubService.GetFileContent after fetching a GitHub content item whose download_url is null or empty. GitHub's contents API omits download_url for some object types (e.g. directories, symlinks, or submodule entries) or returns null when the repo moved/renamed the file. Kavita needs a raw download URL to fetch the file body, so an empty one is treated as an unrecoverable fetch failure.

Source

Thrown at Kavita.Services/ReadingLists/CblGithubService.cs:145

        var item = await BuildApiUrl(normalizedPath)
            .WithGithubHeaders()
            .GetJsonAsync<GithubContentItem>();

        return item.Sha;
    }

    public async Task<string> GetFileContent(string filePath)
    {
        var normalizedPath = NormalizePath(filePath);

        var item = await BuildApiUrl(normalizedPath)
            .WithGithubHeaders()
            .GetJsonAsync<GithubContentItem>();

        if (string.IsNullOrEmpty(item.DownloadUrl))
        {
            throw new KavitaException($"No download URL available for {filePath}");
        }

        return await DownloadByUrl(item.DownloadUrl);
    }

    /// <summary>
    /// Downloads the content of a file
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    private static async Task<string> DownloadByUrl(string url)
    {
        return await url
            .WithGithubHeaders()
            .GetStringAsync();
    }

    private async Task<(List<CblRepoItemDto> Items, GithubRateLimitDto RateLimit)> FetchDirectoryFromGithub(string path)

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Verify filePath points to an actual .cbl/.json file and not a directory using BrowseRepo's IsDirectory flag before calling GetFileContent.
  2. If the file is large, fetch it via the Git blobs API with the sha instead of relying on download_url.
  3. Retry once after a short delay in case the null download_url is a transient GitHub inconsistency.
  4. Check the CBL-ReadingLists repo in a browser to confirm the path still resolves to a file with a raw link.

Example fix

// before
var content = await cblGithubService.GetFileContent(filePath);

// after
var item = await repo.BrowseRepo(parentDir);
var target = item.Items.SingleOrDefault(i => i.Path == filePath);
if (target is null || target.IsDirectory || string.IsNullOrEmpty(target.DownloadUrl))
    return BadRequest($"{filePath} has no downloadable raw content");
var content = await cblGithubService.GetFileContent(filePath);
Defensive patterns

Strategy: validation

Validate before calling

var browse = await cblGithubService.BrowseRepo(Path.GetDirectoryName(filePath));
var item = browse.Items.FirstOrDefault(i => i.Path == filePath);
if (item is null || item.IsDirectory || string.IsNullOrEmpty(item.DownloadUrl))
    return BadRequest($"{filePath} is not downloadable");
var content = await cblGithubService.GetFileContent(filePath);

Type guard

static bool IsDownloadable(CblRepoItemDto item) => !item.IsDirectory && !string.IsNullOrEmpty(item.DownloadUrl);

Try / catch

try { return await cblGithubService.GetFileContent(filePath); }
catch (KavitaException ex) when (ex.Message.Contains("No download URL"))
{ return NotFound($"{filePath} has no raw download URL"); }

Prevention

When it happens

Trigger: Calling GetFileContent(filePath) where filePath resolves to a directory entry, a submodule, a file >100MB (where GitHub requires the blob API), or a path whose content item's download_url field came back null from the REST API.

Common situations: User clicks a folder row in the CBL browser that was mislabeled as a file; the CBL repo added a submodule; a file was force-pushed and the API temporarily returns null download_url; or a path with special characters was normalized incorrectly so it points at the wrong object.

Related errors


AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13). Data as JSON: /api/errors/c32ca37eea059e54. Report an issue: GitHub.