Kareadita/Kavita · warning · KavitaException

invalid-filename

invalid-filename

Error message

invalid-filename

What it means

Thrown by UploadController.CreateThumbnail (localized key 'invalid-filename') when ResolveTempCoverPath returns null — i.e. the FileName either escapes the temp directory (IsPathWithinDirectory fails, blocking path traversal) or does not point to an existing staged file. A KavitaException surfacing as HTTP 500 unless the endpoint catches it.

Source

Thrown at Kavita.Server/Controllers/UploadController.cs:365

    {
        if (!IsPathWithinDirectory(_directoryService.TempDirectory, fileName)) return null;

        var path = _directoryService.FileSystem.Path.Join(_directoryService.TempDirectory, fileName);
        return _directoryService.FileSystem.File.Exists(path) ? path : null;
    }

    private async Task<string> CreateThumbnail(UploadCoverFileDto uploadCoverFileDto, string filename)
    {
        var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
        var encodeFormat = settings.EncodeMediaAs;
        var (width, height) = settings.CoverImageSize.GetDimensions();

        // Preferred path: the image was already streamed into temp (upload-by-url / upload-by-file) and we only
        // received its filename. This avoids posting a large base64 payload back through the request body.
        if (!string.IsNullOrEmpty(uploadCoverFileDto.FileName))
        {
            var tempPath = ResolveTempCoverPath(uploadCoverFileDto.FileName)
                           ?? throw new KavitaException(await _localizationService.TranslateAsync(UserId, "invalid-filename"));

            return _imageService.CreateThumbnailFromFile(tempPath, filename, encodeFormat, width, height);
        }

        // Legacy fallback: base64 payload
        return _imageService.CreateThumbnailFromBase64(uploadCoverFileDto.Url, filename, encodeFormat, width, height);
    }

    /// <summary>
    /// Replaces chapter cover image and locks it with a base64 encoded image. This will update the parent volume's cover image.
    /// </summary>
    /// <param name="uploadCoverFileDto"></param>
    /// <returns></returns>
    [Authorize(Policy = PolicyGroups.AdminPolicy)]
    [RequestSizeLimit(ControllerConstants.MaxUploadSizeBytes)]
    [HttpPost("chapter")]
    public async Task<ActionResult> UploadChapterCoverImageFromUrl(UploadCoverFileDto uploadCoverFileDto)
    {

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Always stage the file into Kavita's temp directory first (upload-by-file/upload-by-url), then send only its base filename.
  2. Send the bare filename, not an absolute or relative path.
  3. Ensure the temp upload completes and persists until the thumbnail request runs.

Example fix

// before
{ "fileName": "../../../etc/passwd" }

// after
{ "fileName": "a1b2c3-cover.jpg" }  // previously staged into temp
Defensive patterns

Strategy: validation

Validate before calling

var path = ResolveTempCoverPath(dto.FileName);
if (path is null) return BadRequest("invalid-filename");

Type guard

static bool IsSafeTempFileName(string? name)
    => !string.IsNullOrWhiteSpace(name)
       && Path.GetFileName(name) == name
       && !name.Contains("..");

Try / catch

try { var p = await CreateThumbnail(dto, file); }
catch (KavitaException ex) { return BadRequest(ex.Message); }

Prevention

When it happens

Trigger: Upload-by-filename cover replace where FileName is missing from temp, contains traversal (../), points outside TempDirectory, or the staged temp upload expired/was cleared before the thumbnail step.

Common situations: Client sends a filename without first streaming the file to temp; path traversal attempt; temp dir cleared between upload and thumbnail creation; wrong filename casing/path from the client.

Related errors


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