{"record":{"id":"e4a69a8b1ca71e9c","repo":"nextcloud/server","slug":"file-is-not-updatable-1-s","errorCode":null,"errorMessage":"File is not updatable: %1$s","messagePattern":"File is not updatable: %1\\$s","errorType":"exception","errorClass":"Sabre\\DAV\\Exception\\ServiceUnavailable","httpStatus":503,"severity":"error","filePath":"apps/dav/lib/Connector/Sabre/File.php","lineNumber":122,"sourceCode":"\t *\n\t * @throws Forbidden\n\t * @throws UnsupportedMediaType\n\t * @throws BadRequest\n\t * @throws Exception\n\t * @throws EntityTooLarge\n\t * @throws ServiceUnavailable\n\t * @throws FileLocked\n\t * @return string|null\n\t */\n\t#[\\Override]\n\tpublic function put($data) {\n\t\ttry {\n\t\t\t$exists = $this->fileView->file_exists($this->path);\n\t\t\tif ($exists && !$this->info->isUpdateable()) {\n\t\t\t\tthrow new Forbidden();\n\t\t\t}\n\t\t} catch (StorageNotAvailableException $e) {\n\t\t\tthrow new ServiceUnavailable($this->l10n->t('File is not updatable: %1$s', [$e->getMessage()]));\n\t\t}\n\n\t\t// verify path of the target\n\t\t$this->verifyPath();\n\n\t\t[$partStorage] = $this->fileView->resolvePath($this->path);\n\t\tif ($partStorage === null) {\n\t\t\tthrow new ServiceUnavailable($this->l10n->t('Failed to get storage for file'));\n\t\t}\n\t\t$needsPartFile = $partStorage->needsPartFile() && (strlen($this->path) > 1);\n\n\t\t$view = Filesystem::getView();\n\n\t\tif ($needsPartFile) {\n\t\t\t$transferId = \\rand();\n\t\t\t// mark file as partial while uploading (ignored by the scanner)\n\t\t\t$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . $transferId . '.part';\n","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/Connector/Sabre/File.php#L104-L140","documentation":"At the start of PUT, file_exists() on the file's view raised StorageNotAvailableException — the backend holding the file (external storage: SMB, SFTP, S3, ...) is unavailable. It is wrapped as ServiceUnavailable (HTTP 503) with the message 'File is not updatable: <backend message>'. Despite the wording, this signals storage unavailability, not a permission problem.","triggerScenarios":"PUT to a file on an external storage that is offline, has expired credentials, or exceeds backend timeouts; misconfigured mounts whose storage cannot even stat the path.","commonSituations":"SMB server down; SFTP credential or host-key rotation breaking auth; S3 signature errors after key changes; slow mounts tripping availability checks.","solutions":["Check the external storage entry in the admin UI (it reports availability) and nextcloud.log for the underlying storage exception.","Repair the backend: restore the server, update credentials, fix mount options.","Retry the PUT once the storage is healthy; clients should treat 503 as retryable with backoff."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    $client->request('PUT', $fileUrl, $content);\n} catch (ServiceUnavailable $e) { // 503 'File is not updatable: ...'\n    // backend storage down — keep the content and retry later\n    queueForRetryWithBackoff($fileUrl, $content);\n}","preventionTips":["Monitor external storage availability; a 503 on PUT usually means the backend is down.","Rotate storage credentials on both sides simultaneously to avoid auth gaps.","Make uploads idempotent and retryable with backoff on 503."],"tags":["dav","files","external-storage","service-unavailable"],"backgroundTag":"storage-unavailable","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}