⚠️ This issue respects the following points: ⚠️
Bug description
On primary object storage (S3) with a finite per-user quota, uploading any file large enough to be chunked fails with HTTP 413 and:
<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns">
<s:exception>OCA\DAV\Connector\Sabre\Exception\EntityTooLarge</s:exception>
<s:message>Insufficient space</s:message>
</d:error>
The rejection is false: in my case a ~250 MB upload was refused while the user had ~8.4 GiB of free quota (10 GiB quota, 1.57 GiB used, verified consistent via occ user:info and an occ files:scan that reported zero changes). Two things confirm it is not a real space limit:
- Reducing the chunk size (
files.chunked_upload.max_size) has no effect, even down to 500 KiB.
- Setting the user's quota to
Unlimited makes the upload succeed immediately.
Root cause
The chunked-upload parts are written to the uploads/ path of the quota-wrapped home storage. In lib/private/Files/Storage/Wrapper/Quota.php:
free_space() intentionally exempts uploads/ and cache/ paths by returning the wrapped storage's value (lines 82-83).
- On object-store primary,
ObjectStoreStorage::free_space() returns FileInfo::SPACE_UNLIMITED (-3) when no total size limit is configured.
writeStream() then runs (around lines 232-236):
if ($size !== null) {
if ($size < $free) { // e.g. 262144000 < -3 => false
return parent::writeStream($path, $stream, $size);
} else {
throw new NotEnoughSpaceException(); // <-- wrongly thrown
}
}
File::convertToSabreException() converts NotEnoughSpaceException into EntityTooLarge (413, "Insufficient space").
writeStream() is the only write method in this wrapper that omits the $free < 0 short-circuit. The siblings handle the negative sentinel correctly:
file_put_contents() line 104: if ($free < 0 || strlen($data) < $free)
copy() line 117: if ($free < 0 || $this->getSize($source) < $free)
copyFromStorage() line 171 and moveFromStorage() line 184: same $free < 0 || guard
fopen() only wraps the quota stream when $free >= 0
The DAV-level QuotaPlugin::checkQuota() also treats negative free space as allowed (if ($freeSpace === false || $freeSpace < 0) { return true; }). Only Quota::writeStream() treats a negative "unlimited / unknown / not-computed" free space as "no space left".
This explains every observed symptom:
- 413 "Insufficient space" with the user far below quota: the comparison is against
-3, not against real remaining quota.
- Chunk size is irrelevant (500 KiB still fails): any positive size is
< -3 => false.
Unlimited quota works: hasQuota() returns false (line 56), so writeStream() short-circuits to the wrapped storage before reaching the faulty branch.
- Object-store specific: on local primary,
Local::free_space() for the uploads path returns a real positive number, so $size < $free passes; only object stores return the negative SPACE_UNLIMITED sentinel here.
Proposed fix
Mirror the guard used by the sibling methods in Quota::writeStream():
if ($size !== null) {
if ($free < 0 || $size < $free) {
return parent::writeStream($path, $stream, $size);
}
throw new NotEnoughSpaceException();
}
Steps to reproduce
- Configure Nextcloud with S3 (or any object store) as primary storage, with no
objectstore total size limit.
- Set a finite quota on a user (e.g. 10 GB) and leave most of it free.
- From the web UI, upload a file larger than the chunk threshold (e.g. 250 MB) into that user's own files.
- Upload fails with HTTP 413
OCA\DAV\Connector\Sabre\Exception\EntityTooLarge / "Insufficient space".
- Lower
files.chunked_upload.max_size (e.g. to 5 MB, then 500 KB): still fails.
- Set the user's quota to
Unlimited: upload succeeds.
Expected behavior
A chunked upload into a folder where the user is well within quota should succeed. A free_space() value of SPACE_UNLIMITED / SPACE_UNKNOWN / SPACE_NOT_COMPUTED (any negative value) must be treated as "allowed", consistent with the other Quota wrapper methods and with QuotaPlugin::checkQuota().
Nextcloud Server version
34
Operating system
Debian/Ubuntu
PHP engine version
PHP 8.4
Web server
Apache (supported)
Database engine version
MariaDB
Is this bug present after an update or on a fresh install?
No response
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
What user-backends are you using?
Configuration report
{
"system": {
"htaccess.RewriteBase": "\/",
"memcache.local": "\\OC\\Memcache\\APCu",
"apps_paths": [
{
"path": "\/var\/www\/html\/apps",
"url": "\/apps",
"writable": false
},
{
"path": "\/var\/www\/html\/custom_apps",
"url": "\/custom_apps",
"writable": true
}
],
"upgrade.disable-web": true,
"instanceid": "***REMOVED SENSITIVE VALUE***",
"objectstore": {
"class": "\\OC\\Files\\ObjectStore\\S3",
"arguments": {
"bucket": "16bb3dac",
"region": "eu-central-1",
"hostname": "manually censored",
"port": "443",
"storageClass": "",
"objectPrefix": "urn:oid:",
"autocreate": true,
"use_ssl": true,
"use_path_style": true,
"legacy_auth": false,
"key": "***REMOVED SENSITIVE VALUE***",
"secret": "***REMOVED SENSITIVE VALUE***"
}
},
"passwordsalt": "***REMOVED SENSITIVE VALUE***",
"secret": "***REMOVED SENSITIVE VALUE***",
"trusted_domains": [
"cloud.aux-systemhaus.de"
],
"datadirectory": "***REMOVED SENSITIVE VALUE***",
"dbtype": "mysql",
"version": "34.0.0.12",
"overwrite.cli.url": "https:\/\/cloud.aux-systemhaus.de",
"overwriteprotocol": "https",
"dbname": "***REMOVED SENSITIVE VALUE***",
"dbhost": "***REMOVED SENSITIVE VALUE***",
"dbport": "",
"dbtableprefix": "oc_",
"mysql.utf8mb4": true,
"dbuser": "***REMOVED SENSITIVE VALUE***",
"dbpassword": "***REMOVED SENSITIVE VALUE***",
"installed": true,
"mail_smtpmode": "smtp",
"mail_sendmailmode": "smtp",
"mail_from_address": "***REMOVED SENSITIVE VALUE***",
"mail_domain": "***REMOVED SENSITIVE VALUE***",
"mail_smtphost": "***REMOVED SENSITIVE VALUE***",
"mail_smtpport": "465",
"mail_smtptimeout": 30,
"mail_smtpauth": true,
"mail_smtpname": "***REMOVED SENSITIVE VALUE***",
"mail_smtppassword": "***REMOVED SENSITIVE VALUE***",
"mail_smtpsecure": "ssl",
"trusted_proxies": "***REMOVED SENSITIVE VALUE***",
"loglevel": 0,
"maintenance": false,
"simpleSignUpLink.shown": false,
"files.chunked_upload.max_size": 20971520
}
}
List of activated Apps
Enabled:
- activity: 7.0.0
- app_api: 34.0.0
- appstore: 1.0.0
- bruteforcesettings: 7.0.0
- calendar: 6.4.2
- circles: 34.0.0
- cloud_federation_api: 1.18.0
- comments: 1.24.0
- contacts: 8.7.0
- contactsinteraction: 1.15.0
- dashboard: 7.14.0
- dav: 1.39.0
- federatedfilesharing: 1.24.0
- federation: 1.24.0
- files: 2.6.0
- files_downloadlimit: 5.2.0-dev.0
- files_lock: 34.0.0
- files_pdfviewer: 7.0.0-dev.0
- files_reminders: 1.7.0
- files_sharing: 1.26.0
- files_trashbin: 1.24.0
- files_versions: 1.27.0
- firstrunwizard: 7.0.0-dev.0
- forms: 5.3.1
- logreader: 7.0.0
- lookup_server_connector: 1.22.0
- mail: 5.9.2
- nextcloud_announcements: 6.0.0
- notes: 6.0.0
- notifications: 7.0.0-dev.1
- oauth2: 1.22.0
- office: 1.0.0
- password_policy: 6.0.0-dev.0
- photos: 7.0.0
- privacy: 6.0.0-dev.1
- profile: 1.3.0
- provisioning_api: 1.24.0
- recommendations: 7.0.0-dev.0
- related_resources: 5.0.0-dev.0
- richdocuments: 11.0.0
- richdocumentscode: 26.4.104
- serverinfo: 6.0.0
- settings: 1.17.0
- sharebymail: 1.24.0
- spreed: 24.0.0
- support: 6.0.0
- survey_client: 6.0.0-dev.0
- systemtags: 1.24.0
- text: 8.0.0
- theming: 2.9.0
- twofactor_backupcodes: 1.23.0
- twofactor_totp: 16.0.0
- updatenotification: 1.24.0
- user_status: 1.14.0
- viewer: 7.0.0-dev.0
- weather_status: 1.14.0
- webhook_listeners: 1.6.0
- workflowengine: 2.16.0
Disabled:
- admin_audit: 1.24.0
- drawio: 4.2.3 (installed 4.2.3)
- encryption: 2.22.0
- files_external: 1.26.0
- secrets: 3.0.3 (installed 3.0.3)
- suspicious_login: 12.0.0-dev.0
- twofactor_nextcloud_notification: 8.0.0
- user_ldap: 1.25.0
Nextcloud Signing status
Technical information
=====================
The following list covers which files have failed the integrity check. Please read
the previous linked documentation to learn more about the errors and how to fix
them.
Results
=======
- core
- INVALID_HASH
- core/js/mimetypelist.js
- EXTRA_FILE
- core/img/filetypes/drawio.svg
- core/img/filetypes/dwb.svg
Raw output
==========
Array
(
[core] => Array
(
[INVALID_HASH] => Array
(
[core/js/mimetypelist.js] => Array
(
[expected] => cb945c6402e12d9e7d42d0359acf95a6e9a9b0c1f3bd8528f598a7fb1694e5ae34c80cf44ef6c8901eac1bfdd152de3315fc7eac007efee0f33f09ed3e518b6a
[current] => 09779c15c6ca51e1dd07931ef2708756cbdc9a9adb41cd6a12eb177f57e005652e1ed3e1df18d46f80a4ad30637a6075e7a9cc11f34b44aeb66b3631b8ba1840
)
)
[EXTRA_FILE] => Array
(
[core/img/filetypes/drawio.svg] => Array
(
[expected] =>
[current] => 92e0974cf869bf8ab969c3442dc2b80d55fde36441d22924db74916a06b407520aa2a9dc39336f9157195ebede697ffac0e639360879255ab91932d406e1897d
)
[core/img/filetypes/dwb.svg] => Array
(
[expected] =>
[current] => 43731dd5f17a048112ea5109b40b02ec019b3ee2324385a0f448e3bd2264cb13dc160ab018d893f92f8e2f168fd09009b51578c8c6b97a02a1617c67ac087701
)
)
)
)
Nextcloud Logs
Additional info
No response
Bug description
On primary object storage (S3) with a finite per-user quota, uploading any file large enough to be chunked fails with HTTP 413 and:
The rejection is false: in my case a ~250 MB upload was refused while the user had ~8.4 GiB of free quota (10 GiB quota, 1.57 GiB used, verified consistent via
occ user:infoand anocc files:scanthat reported zero changes). Two things confirm it is not a real space limit:files.chunked_upload.max_size) has no effect, even down to 500 KiB.Unlimitedmakes the upload succeed immediately.Root cause
The chunked-upload parts are written to the
uploads/path of the quota-wrapped home storage. Inlib/private/Files/Storage/Wrapper/Quota.php:free_space()intentionally exemptsuploads/andcache/paths by returning the wrapped storage's value (lines 82-83).ObjectStoreStorage::free_space()returnsFileInfo::SPACE_UNLIMITED(-3) when no total size limit is configured.writeStream()then runs (around lines 232-236):File::convertToSabreException()convertsNotEnoughSpaceExceptionintoEntityTooLarge(413, "Insufficient space").writeStream()is the only write method in this wrapper that omits the$free < 0short-circuit. The siblings handle the negative sentinel correctly:file_put_contents()line 104:if ($free < 0 || strlen($data) < $free)copy()line 117:if ($free < 0 || $this->getSize($source) < $free)copyFromStorage()line 171 andmoveFromStorage()line 184: same$free < 0 ||guardfopen()only wraps the quota stream when$free >= 0The DAV-level
QuotaPlugin::checkQuota()also treats negative free space as allowed (if ($freeSpace === false || $freeSpace < 0) { return true; }). OnlyQuota::writeStream()treats a negative "unlimited / unknown / not-computed" free space as "no space left".This explains every observed symptom:
-3, not against real remaining quota.< -3=> false.Unlimitedquota works:hasQuota()returns false (line 56), sowriteStream()short-circuits to the wrapped storage before reaching the faulty branch.Local::free_space()for the uploads path returns a real positive number, so$size < $freepasses; only object stores return the negativeSPACE_UNLIMITEDsentinel here.Proposed fix
Mirror the guard used by the sibling methods in
Quota::writeStream():Steps to reproduce
objectstoretotal size limit.OCA\DAV\Connector\Sabre\Exception\EntityTooLarge/ "Insufficient space".files.chunked_upload.max_size(e.g. to 5 MB, then 500 KB): still fails.Unlimited: upload succeeds.Expected behavior
A chunked upload into a folder where the user is well within quota should succeed. A
free_space()value ofSPACE_UNLIMITED/SPACE_UNKNOWN/SPACE_NOT_COMPUTED(any negative value) must be treated as "allowed", consistent with the otherQuotawrapper methods and withQuotaPlugin::checkQuota().Nextcloud Server version
34
Operating system
Debian/Ubuntu
PHP engine version
PHP 8.4
Web server
Apache (supported)
Database engine version
MariaDB
Is this bug present after an update or on a fresh install?
No response
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
What user-backends are you using?
Configuration report
{ "system": { "htaccess.RewriteBase": "\/", "memcache.local": "\\OC\\Memcache\\APCu", "apps_paths": [ { "path": "\/var\/www\/html\/apps", "url": "\/apps", "writable": false }, { "path": "\/var\/www\/html\/custom_apps", "url": "\/custom_apps", "writable": true } ], "upgrade.disable-web": true, "instanceid": "***REMOVED SENSITIVE VALUE***", "objectstore": { "class": "\\OC\\Files\\ObjectStore\\S3", "arguments": { "bucket": "16bb3dac", "region": "eu-central-1", "hostname": "manually censored", "port": "443", "storageClass": "", "objectPrefix": "urn:oid:", "autocreate": true, "use_ssl": true, "use_path_style": true, "legacy_auth": false, "key": "***REMOVED SENSITIVE VALUE***", "secret": "***REMOVED SENSITIVE VALUE***" } }, "passwordsalt": "***REMOVED SENSITIVE VALUE***", "secret": "***REMOVED SENSITIVE VALUE***", "trusted_domains": [ "cloud.aux-systemhaus.de" ], "datadirectory": "***REMOVED SENSITIVE VALUE***", "dbtype": "mysql", "version": "34.0.0.12", "overwrite.cli.url": "https:\/\/cloud.aux-systemhaus.de", "overwriteprotocol": "https", "dbname": "***REMOVED SENSITIVE VALUE***", "dbhost": "***REMOVED SENSITIVE VALUE***", "dbport": "", "dbtableprefix": "oc_", "mysql.utf8mb4": true, "dbuser": "***REMOVED SENSITIVE VALUE***", "dbpassword": "***REMOVED SENSITIVE VALUE***", "installed": true, "mail_smtpmode": "smtp", "mail_sendmailmode": "smtp", "mail_from_address": "***REMOVED SENSITIVE VALUE***", "mail_domain": "***REMOVED SENSITIVE VALUE***", "mail_smtphost": "***REMOVED SENSITIVE VALUE***", "mail_smtpport": "465", "mail_smtptimeout": 30, "mail_smtpauth": true, "mail_smtpname": "***REMOVED SENSITIVE VALUE***", "mail_smtppassword": "***REMOVED SENSITIVE VALUE***", "mail_smtpsecure": "ssl", "trusted_proxies": "***REMOVED SENSITIVE VALUE***", "loglevel": 0, "maintenance": false, "simpleSignUpLink.shown": false, "files.chunked_upload.max_size": 20971520 } }List of activated Apps
Nextcloud Signing status
Nextcloud Logs
Additional info
No response