Skip to content

[Bug]: WebDAV chunked uploads fail with S3 primary storage and per-user quota #61704

Description

@veryCrunchy

Summary

WebDAV chunked uploads fail immediately with 413 Request Entity Too Large / Insufficient space when Nextcloud is configured with S3 primary object storage and the affected user has a finite quota.

The account still has plenty of quota available, direct non-chunked WebDAV uploads work, and Nextcloud reports the destination folder has free space. The failure appears to happen because OC\Files\Storage\Wrapper\Quota::writeStream() receives an upload staging path under uploads/..., delegates free_space() to the underlying object-store storage, and then treats the object store's unknown free-space result as not enough space.

Related Issue / Disclosure

After diagnosing this locally and preparing a fix, I found that this appears to match the already reported issue #61488. I am adding this as an additional reproduction and validation report rather than claiming this is a separate discovery.

AI assistance was used to help inspect logs, compare code paths, draft reproduction notes, and run/organize validation steps. The server-side behavior, failing logs, patch, and validation were checked against an actual affected Nextcloud instance.

Environment

  • Nextcloud: 34.0.1.2
  • Deployment: containerized / AIO-style deployment
  • Storage: S3 primary object storage
  • S3 provider: S3-compatible object storage
  • WebDAV client: rclone v1.73.0
  • User quota: finite quota configured
  • Free quota reported by Nextcloud for the affected user: significantly larger than the test upload
  • Host/container local disk also has sufficient free space

Relevant system config shape:

'objectstore' => [
  'class' => '\\OC\\Files\\ObjectStore\\S3',
  'arguments' => [
    'bucket' => '<redacted>',
    'region' => '<redacted>',
    'hostname' => '<redacted>',
    'use_path_style' => true,
    'uploadPartSize' => 268435456,
    'concurrency' => 10,
    // credentials redacted
  ],
],
'files.chunked_upload.max_size' => 268435456,
'files.chunked_upload.max_parallel_count' => 10,

Expected Behavior

Chunked WebDAV uploads should succeed when the authenticated user has enough quota available.

For example, uploading a small test file in WebDAV chunks should succeed when the user has much more free quota than the upload size.

Actual Behavior

The first chunk fails:

uploading chunk failed: Insufficient space: OCA\DAV\Connector\Sabre\Exception\EntityTooLarge: 413 Request Entity Too Large

The same destination accepts direct, non-chunked WebDAV uploads.

Reproduction

With S3 primary object storage and a finite per-user quota configured:

dd if=/dev/urandom of=/tmp/nc-rclone-chunk-test.bin bs=1M count=6

rclone copyto /tmp/nc-rclone-chunk-test.bin :webdav:test-folder/.chunk-test.bin \
  --webdav-url=https://example.invalid/remote.php/dav/files/testuser \
  --webdav-vendor=nextcloud \
  --webdav-user=testuser \
  --webdav-pass='<obscured app password>' \
  --webdav-nextcloud-chunk-size=5Mi \
  --retries=1 \
  --low-level-retries=1 \
  --log-level=INFO

Observed result:

Failed to copy: uploading chunk failed: Insufficient space:
OCA\DAV\Connector\Sabre\Exception\EntityTooLarge: 413 Request Entity Too Large

Control test:

rclone copyto /tmp/nc-rclone-chunk-test.bin :webdav:test-folder/.direct-test.bin \
  --webdav-url=https://example.invalid/remote.php/dav/files/testuser \
  --webdav-vendor=nextcloud \
  --webdav-user=testuser \
  --webdav-pass='<obscured app password>' \
  --webdav-nextcloud-chunk-size=0

The direct/non-chunked upload succeeds.

Server-Side Evidence

Nextcloud reports the user and destination folder have free space greater than the test upload:

/ free=<large-positive-number>
test-folder free=<large-positive-number>

But the chunk upload fails in the quota wrapper while writing to the upload staging area:

Exception: OCP\Files\NotEnoughSpaceException
File: /var/www/html/lib/private/Files/Storage/Wrapper/Quota.php
Line: 235

URL: /remote.php/dav/uploads/testuser/rclone-chunked-upload-.../000000000000000-000000005242879

Relevant stack frames:

OC\Files\Storage\Wrapper\Quota->writeStream(
  'uploads/rclone-chunked-upload-.../000000000000000-000000005242879',
  ...,
  5242880
)

OCA\DAV\Connector\Sabre\File->put(...)
OCA\DAV\Connector\Sabre\Directory->createFile(...)
OCA\DAV\Upload\UploadFolder->createFile(...)

Suspected Cause

In lib/private/Files/Storage/Wrapper/Quota.php:

public function free_space(string $path): int|float|false {
    if (!$this->hasQuota()) {
        return $this->getWrapperStorage()->free_space($path);
    }
    if ($this->getQuota() < 0 || str_starts_with($path, 'cache') || str_starts_with($path, 'uploads')) {
        return $this->getWrapperStorage()->free_space($path);
    }
    // ...
}

public function writeStream(string $path, $stream, ?int $size = null): int {
    // ...
    $free = $this->free_space($path);
    // ...
    if ($size !== null) {
        if ($size < $free) {
            return parent::writeStream($path, $stream, $size);
        } else {
            throw new NotEnoughSpaceException();
        }
    }
}

For paths under uploads/..., free_space() bypasses quota accounting and asks the wrapped object-store storage directly. With S3 primary storage, free space is not a meaningful local value, so the result can be unsuitable for the strict $size < $free check in writeStream().

The destination folder's quota-aware free space is correct, but the upload staging path's object-store free-space result causes an immediate false NotEnoughSpaceException.

Additional Notes

  • Direct non-chunked WebDAV upload works.
  • Chunked upload with the correct user id in the WebDAV URL and auth user still fails.
  • The failure is not caused by PHP upload limits or proxy max body size; those are configured much higher than the test file.
  • The failure occurs before S3 multipart completion.
  • Applying the one-line Quota::writeStream() change from the linked PR resolved the 413 Insufficient space failure on the affected instance and allowed chunked WebDAV uploads to proceed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    To triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions