duplicati/duplicati · error · InvalidOperationException

Invalid file size for {remotename}

Error message

Invalid file size for {remotename}

What it means

Thrown in ParallelGetAsync when info.Size is negative. ToFileEntry sets size to -1 when the <size> element is missing or not a parseable long, so a < 0 size means the file metadata is absent or corrupt.

Source

Thrown at Duplicati/Library/Backend/Jottacloud/Jottacloud.cs:468

        await using var s = await response.Content.ReadAsStreamAsync(cancelToken).ConfigureAwait(false);
        await using var t = s.ObserveReadTimeout(m_timeouts.ReadWriteTimeout);
        await Utility.Utility.CopyStreamAsync(s, stream, true, cancelToken).ConfigureAwait(false);
    }

    /// <summary>
    /// Fetches the file in chunks (parallelized)
    /// </summary>
    private async Task ParallelGetAsync(string remotename, Stream stream, CancellationToken cancelToken)
    {
        // Get file info and validate
        var info = await Info(remotename, cancelToken).ConfigureAwait(false);
        if (info == null)
            throw new FileMissingException(remotename);

        var size = info.Size;
        if (size < 0)
            throw new InvalidOperationException($"Invalid file size for {remotename}");

        // Calculate chunks with bounds checking
        var chunks = new Queue<(long start, long end)>();
        long position = 0;
        while (position < size)
        {
            var length = Math.Min(m_chunksize, size - position);
            if (length <= 0)
                throw new InvalidOperationException($"Invalid chunk length calculated for {remotename}");
            chunks.Enqueue((position, position + length));
            position += length;
        }

        var tasks = new List<Task<(byte[] buffer, long start, Exception? error)>>();
        var completedChunks = new SortedDictionary<long, byte[]>();
        var semaphore = new SemaphoreSlim(m_threads, m_threads);
        long nextWritePosition = 0;

View on GitHub (pinned to 3f348be3e3)

Solutions

  1. Re-upload the file to regenerate clean metadata.
  2. Fall back to single-threaded download (jottacloud-threads=1) which does not need the size for chunking.
  3. Inspect the raw XML for the file via a direct JFS GET to confirm the size element.

Example fix

// before
--jottacloud-threads=4  // ParallelGetAsync needs a valid size
// after
--jottacloud-threads=1  // GetAsync streams without chunk math
Defensive patterns

Strategy: fallback

Validate before calling

// Prefer single-threaded get when file metadata is unreliable
if (threads <= 1) await backend.GetAsync(name, dest, token); // streams without size math

Try / catch

try { await backend.GetAsync(remotename, dest, token); /* threads > 1 */ }
catch (InvalidOperationException ex) when (ex.Message.Contains("Invalid file size"))
{
    // fall back to single-threaded streaming download that does not need the size
}

Prevention

When it happens

Trigger: The file's currentRevision XML lacks a <size> child or contains a non-numeric <size>, causing ToFileEntry to default size to -1, which then fails the size >= 0 guard before chunking.

Common situations: Server-side metadata corruption; the file is in an unusual state (e.g. only a corrupt latestRevision); a JFS API response-format change dropped or renamed the size element.

Related errors


AI-assisted analysis of duplicati/duplicati@3f348be3e3 (2026-08-13). Data as JSON: /api/errors/c327218a4152276a. Report an issue: GitHub.