Skip to content

🐛 Daily maintenance crashes on console project: Collection not found from deleteOldDeployments (self-hosted 1.9.x) #12230

@OjusWiZard

Description

@OjusWiZard

👟 Reproduction steps

On any self-hosted Appwrite 1.9.x installation, every daily maintenance run (default _APP_MAINTENANCE_START_TIME=00:00) emits an unhandled Utopia\Database\Exception\NotFound: Collection not found from appwrite-queue-v1-deletes. The error fires once per maintenance tick — predictably at midnight UTC for default-configured installs — and lights up Sentry / error logs every single day.

To reproduce on a clean install:

  1. Install Appwrite 1.9.x and let appwrite-task-maintenance enqueue its daily job.
  2. Watch appwrite-worker-deletes logs at the next _APP_MAINTENANCE_START_TIME.
  3. Observe the exception below.

👍 Expected behavior

Daily maintenance completes silently. deleteOldDeployments should either skip projects whose schema does not declare the functions / sites collections, or maintenance should not enqueue this delete type for the console project.

👎 Actual Behavior

Every maintenance tick throws:

Utopia\Database\Exception\NotFound: Collection not found
  at /usr/src/code/vendor/utopia-php/database/src/Database/Database.php:8292
  Utopia\Database\Database->find('functions', [...])
  at /usr/src/code/src/Appwrite/Platform/Workers/Deletes.php:1469 (listByGroup)
  at /usr/src/code/src/Appwrite/Platform/Workers/Deletes.php:420 (deleteOldDeployments)
  at /usr/src/code/src/Appwrite/Platform/Workers/Deletes.php:218 (action — DELETE_TYPE_MAINTENANCE)

Root cause

Maintenance.php:62-91 enqueues a DELETE_TYPE_MAINTENANCE job for every accessed tenant project and explicitly for the $console project itself:

$queueForDeletes
    ->setType(DELETE_TYPE_MAINTENANCE)
    ->setProject($console)
    ...
    ->trigger();

The handler at Deletes.php:218 routes that into deleteOldDeployments, which at :420 calls:

$this->listByGroup('functions', [], $dbForProject, $removalCallback);
$this->listByGroup('sites',     [], $dbForProject, $removalCallback);

listByGroup then runs $database->find($collection, …) (:1469), which throws NotFoundException at Database.php:8292 because the console schema (defined in app/config/collections/platform.php) does not declare functions, sites, or deployments collections — those are tenant-project collections defined in app/config/collections/projects.php.

Verification

On the affected install (single tenant project + console):

-- tenant project (works):
SELECT _uid FROM _5__metadata WHERE _uid IN ('functions','sites','deployments');
-- returns: deployments, functions, sites

-- console project (throws):
SELECT _uid FROM _console__metadata WHERE _uid IN ('functions','sites','deployments');
-- returns: (empty)

Tenant-project maintenance succeeds; console-project maintenance throws on every tick.

Likely regression

Introduced in #10959 — Feat: Auto-delete deployments (merged Dec 2025), shipped via the schema-migration bundle #10862. The migration set up tenant-project collections but the new cross-project maintenance loop targets the console project as well, where these collections were never expected to exist.

🤖 Suggested fix

Either of these changes resolves it cleanly:

Option A — guard deleteOldDeployments (smallest change). In Workers/Deletes.php::deleteOldDeployments, skip when the schema does not declare the collection:

foreach (['functions', 'sites'] as $group) {
    if ($dbForProject->getCollection($group)->isEmpty()) {
        continue;
    }
    $this->listByGroup($group, [], $dbForProject, $removalCallback);
}

Option B — don't enqueue the console job for this delete type. In Tasks/Maintenance.php, the per-project loop already covers tenant projects; the explicit $console enqueue (lines 89-91) is for console-only cleanup tasks (audit, cache, schedules) and shouldn't trigger deleteOldDeployments. Either split DELETE_TYPE_MAINTENANCE into a console-scoped subtype, or have the Deletes worker branch on $project->getId() === 'console' and skip deployment cleanup.

I'd lean toward A because it's the most defensive: it also protects against any future per-project schema drift where a project might legitimately not host functions/sites.

🎲 Versions

  • Appwrite: 1.9.x (self-hosted)
  • Reproduces on default _APP_MAINTENANCE_START_TIME=00:00, _APP_MAINTENANCE_INTERVAL=86400
  • Single-tenant install — but happens with any number of tenants because the issue is on the console-project iteration

➕ Additional Information

Related upstream issue I previously filed against the same release line: #12200 (scheduler localhost endpoint). Happy to send a PR for either suggested fix above if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    product / consoleConsole, UI and UX issuesproduct / databasesFixes and upgrades for the Appwrite Database.product / functionsFixes and upgrades for the Appwrite Functions.product / messagingFixes and upgrades for the Appwrite Messaging.product / migrationsFixes and upgrades for the Appwrite Migrations.product / self-hostedIssues only found when self-hosting Appwriteproduct / sitesFixes and upgrades for Appwrite Sites.product / storageFixes and upgrades for the Appwrite Storage.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions