{"record":{"id":"a9742720e4677b36","repo":"nextcloud/server","slug":"could-not-remove-app","errorCode":null,"errorMessage":"could not remove app","messagePattern":"could not remove app","errorType":"http","errorClass":"OCSException","httpStatus":500,"severity":"error","filePath":"apps/appstore/lib/Controller/ApiController.php","lineNumber":241,"sourceCode":"\t * 200: App successfully uninstalled\n\t */\n\t#[PasswordConfirmationRequired(strict: true)]\n\t#[ApiRoute(verb: 'POST', url: '/api/v1/apps/uninstall')]\n\tpublic function uninstallApp(string $appId): DataResponse {\n\t\t$appId = $this->appManager->cleanAppId($appId);\n\t\tif ($this->appManager->isEnabledForAnyone($appId)) {\n\t\t\t$this->disableApp($appId);\n\t\t}\n\n\t\t$result = $this->installer->removeApp($appId);\n\t\tif ($result !== false) {\n\t\t\t// If this app was force enabled, remove the force-enabled-state\n\t\t\t$this->appManager->removeOverwriteNextcloudRequirement($appId);\n\t\t\t$this->appManager->clearAppsCache();\n\t\t\treturn new DataResponse([]);\n\t\t}\n\n\t\tthrow new OCSException('could not remove app', Http::STATUS_INTERNAL_SERVER_ERROR);\n\t}\n\n\t/**\n\t * Update an app\n\t *\n\t * @param string $appId - The app to update\n\t * @return DataResponse<Http::STATUS_OK, array{}, array{}>\n\t * @throws OCSException - if the app could not be updated\n\t *\n\t * 200: App successfully updated\n\t */\n\t#[PasswordConfirmationRequired(strict: true)]\n\t#[ApiRoute(verb: 'POST', url: '/api/v1/apps/update')]\n\tpublic function updateApp(string $appId): DataResponse {\n\t\t$appId = $this->appManager->cleanAppId($appId);\n\n\t\t$this->config->setSystemValue('maintenance', true);\n\t\ttry {","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/appstore/lib/Controller/ApiController.php#L223-L259","documentation":"OCSException (HTTP 500) thrown by ApiController::uninstallApp when installer->removeApp($appId) returns false (not when it throws). The controller first disables the app if enabled, then removes files and clears caches; a boolean false from removeApp means the app could not be uninstalled and the response carries no chained exception.","triggerScenarios":"POST /ocs/appstore/apps/uninstall where removeApp returns false: app directory not present under apps/, files not deletable (permissions on apps/), or the app is shipped with the release and cannot be file-removed (only third-party apps are removable).","commonSituations":"Attempting to remove a bundled core app (only disable is possible); apps/ owned by root so the web server cannot delete the directory; app already deleted on disk but still registered in the database; leftover state from a previous failed install.","solutions":["Verify the app is third-party (installed under apps/ from the store) — shipped apps can only be disabled, not removed","Check write/delete permission on the apps/ directory for the web server user","Use `occ app:remove <appId>` to see whether the CLI path reports a clearer reason","If the directory is gone but the DB still lists the app, clear caches / check oc_appconfig entries"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n\tawait axios.post(generateOcsUrl('/apps/appstore/apps/uninstall'), { appId })\n} catch (e) {\n\t// 500 'could not remove app' — removeApp returned false: not present on disk, perms, or a shipped app\n}","preventionTips":["Offer uninstall only for third-party apps; shipped apps can merely be disabled","Keep apps/ writable by the web server user so removal can delete files","After failed uninstalls, verify on disk whether the app directory still exists before retrying"],"tags":["appstore","apps","uninstall","php","ocs"],"backgroundTag":"app-uninstall-failed","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}