duplicati/duplicati · error · UserInformationException

HandleWriteError

HandleWriteError

Error message

Failed to write to file, difference between bytes read and bytes written

What it means

Thrown inside SMBShareConnection.PutAsync during the chunked file-upload loop. After calling _smbFileStore.WriteFile, the code checks that numberOfBytesWritten equals bytesRead for that chunk. SMB servers can report fewer bytes written than the chunk submitted (a partial write), which this guard treats as a hard failure rather than silently corrupting the uploaded file. The error code is 'HandleWriteError'.

Source

Thrown at Duplicati/Library/Backend/SMB/SMBShareConnection.cs:483

            }).ConfigureAwait(false);

            if (status == NTStatus.STATUS_SUCCESS)
            {
                // Use the provided write buffer size if set, otherwise use the protocol negotiated maximum. Never exceed the negotiated maximum.
                var buffer = new byte[Math.Min(_connectionParameters.WriteBufferSize ?? (int)_smb2Client.MaxWriteSize, _smb2Client.MaxWriteSize)];
                int bytesRead;
                int numberOfBytesWritten;
                int offset = 0;
                using var timeoutStream = sourceStream.ObserveReadTimeout(_timeouts.ReadWriteTimeout, false);
                while (!cancellationToken.IsCancellationRequested && timeoutStream.Position < timeoutStream.Length)
                {
                    bytesRead = await timeoutStream.ReadAsync(buffer, cancellationToken);
                    if (bytesRead == 0)
                        break;
                    status = _smbFileStore.WriteFile(out numberOfBytesWritten, fileHandle, offset, buffer.Take(bytesRead).ToArray());
                    offset += numberOfBytesWritten;
                    if (numberOfBytesWritten != bytesRead)
                        throw new UserInformationException(LC.L("Failed to write to file, difference between bytes read and bytes written"), "HandleWriteError");
                    if (status != NTStatus.STATUS_SUCCESS)
                        throw new UserInformationException($"{LC.L("Failed to write file on Putasync")} {filename} with status {status.ToString()}", "HandleWriteError");
                }
                if (fileHandle != null)
                {
                    status = _smbFileStore.CloseFile(fileHandle);
                    if (status != NTStatus.STATUS_SUCCESS)
                        throw new UserInformationException($"{LC.L("Failed to close file handle on PutAsync")} {filename} with status {status.ToString()}", "HandleCloseError");
                }
            }
            else
            {
                throw new UserInformationException($"{LC.L("Failed to create file for writing")} {filename} with status {status.ToString()}", "FileCreateError");
            }
        }
        finally
        {
            _semaphore.Release();

View on GitHub (pinned to 3f348be3e3)

Solutions

  1. Check the server-side SMB logs for the specific chunk that was partially accepted; reduce the configured WriteBufferSize to stay within the negotiated maximum.
  2. Verify network stability between the Duplicati host and the SMB share (packet loss, MTU fragmentation, VPN MTU clamp).
  3. Update to the latest Duplicati version; this guard exists to detect a protocol-level partial write that earlier builds may have silently ignored.
  4. If targeting Samba, ensure the server version supports SMB 2.0.2+ and that the negotiated dialect matches the client expectations.

Example fix

// before: buffer may exceed negotiated max, server accepts partial chunk
var buffer = new byte[Math.Min(_connectionParameters.WriteBufferSize ?? (int)_smb2Client.MaxWriteSize, _smb2Client.MaxWriteSize)];

// after: clamp explicitly and loop the remainder if server reports fewer bytes
var chunkSize = Math.Min(_connectionParameters.WriteBufferSize ?? (int)_smb2Client.MaxWriteSize, _smb2Client.MaxWriteSize);
// handle partial writes by re-sending the unwritten tail instead of throwing
Defensive patterns

Strategy: validation

Validate before calling

// Before calling PutAsync, verify the configured write buffer size does not exceed the negotiated maximum
var configuredWriteSize = connectionParameters.WriteBufferSize ?? (int)smb2Client.MaxWriteSize;
if (configuredWriteSize > smb2Client.MaxWriteSize)
    throw new InvalidOperationException($"WriteBufferSize {configuredWriteSize} exceeds negotiated max {smb2Client.MaxWriteSize}");

Try / catch

try
{
    await smbBackend.PutAsync(filename, stream, cancellationToken);
}
catch (UserInformationException ex) when (ex.HelpID == "HandleWriteError")
{
    // Partial write detected; the file may be incomplete on the share
    logger.LogWarning("SMB partial write for {File}: {Message}", filename, ex.Message);
    throw; // or retry with a smaller buffer
}

Prevention

When it happens

Trigger: Calling PutAsync on the SMB backend; the WriteFile call succeeds (STATUS_SUCCESS) but returns a numberOfBytesWritten smaller than the bytesRead passed to it for a given buffer chunk. This typically happens with mismatched buffer sizes, connection instability, or a server that limits the accepted write size per call.

Common situations: WriteBufferSize exceeds what the negotiated SMB MaxWriteSize allows; transient network issues causing the server to partially accept a chunk; connecting to a Samba or Windows share with an older dialect that caps per-write byte counts; the buffer is sized larger than the server's negotiated maximum.

Related errors


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