From f50a42bc5141d2ede623042585e3b9b97ae8686a Mon Sep 17 00:00:00 2001 From: Prem Palanisamy Date: Mon, 25 May 2026 11:49:39 +0100 Subject: [PATCH] Wrap user-facing throws in Sources/Appwrite as Migration\Exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four sites in the Appwrite source threw bare \Exception, which bubbles up to the migrations worker's outer catch as an arm-3 (unknown) exception and gets published to Sentry. They are all user-state / user-input causes (missing dbForProject, unknown source type, missing API key scopes, source API errors like "Database reads limit exceeded", deleted team memberships) — Sentry noise rather than engineering signal. Throwing Migration\Exception instead routes them through arm 2 of the worker gate (always suppressed) and onto the migration document's errors[]. --- src/Migration/Sources/Appwrite.php | 37 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Migration/Sources/Appwrite.php b/src/Migration/Sources/Appwrite.php index 97b7ec0d..0f3f7025 100644 --- a/src/Migration/Sources/Appwrite.php +++ b/src/Migration/Sources/Appwrite.php @@ -138,7 +138,12 @@ public function __construct( case static::SOURCE_DATABASE: if (\is_null($dbForProject)) { - throw new \Exception('Database is required for database source', Exception::CODE_VALIDATION); + throw new Exception( + resourceName: '', + resourceGroup: '', + message: 'Database is required for database source', + code: Exception::CODE_VALIDATION, + ); } $this->reader = new DatabaseReader( $this->dbForProject, @@ -148,7 +153,12 @@ public function __construct( break; default: - throw new \Exception('Unknown source', Exception::CODE_VALIDATION); + throw new Exception( + resourceName: '', + resourceGroup: '', + message: 'Unknown source', + code: Exception::CODE_VALIDATION, + ); } } @@ -263,9 +273,21 @@ public function report(array $resources = [], array $resourceIds = []): array )['version']; } catch (\Throwable $e) { if ($e->getCode() === 403) { - throw new \Exception('Missing required scopes.', $e->getCode(), $e); + throw new Exception( + resourceName: '', + resourceGroup: '', + message: 'Missing required scopes.', + code: $e->getCode(), + previous: $e, + ); } else { - throw new \Exception($e->getMessage(), $e->getCode(), $e); + throw new Exception( + resourceName: '', + resourceGroup: '', + message: $e->getMessage(), + code: $e->getCode(), + previous: $e, + ); } } @@ -725,7 +747,12 @@ private function exportMemberships(int $batchSize): void foreach ($response->memberships as $membership) { $user = $cacheUsers[$membership->userId] ?? null; if ($user === null) { - throw new \Exception('User not found', Exception::CODE_NOT_FOUND); + throw new Exception( + resourceName: Resource::TYPE_MEMBERSHIP, + resourceGroup: Transfer::GROUP_AUTH, + message: 'User not found', + code: Exception::CODE_NOT_FOUND, + ); } $memberships[] = new Membership(