{"record":{"id":"d0f9fe4f56553643","repo":"monicahq/monica","slug":"e-getmessage","errorCode":null,"errorMessage":"$e->getMessage()","messagePattern":"\\$e->getMessage\\(\\)","errorType":"http","errorClass":"BadRequestHttpException","httpStatus":400,"severity":"error","filePath":"app/Domains/Contact/ManageDocuments/Listeners/DeleteFileInStorage.php","lineNumber":61,"sourceCode":"    {\n        if (is_null(config('services.uploadcare.private_key'))) {\n            throw new EnvVariablesNotSetException;\n        }\n\n        if (is_null(config('services.uploadcare.public_key'))) {\n            throw new EnvVariablesNotSetException;\n        }\n    }\n\n    private function getFileFromUploadcare(): void\n    {\n        $configuration = Configuration::create(config('services.uploadcare.public_key'), config('services.uploadcare.private_key'));\n        $this->api = new Api($configuration);\n\n        try {\n            $this->fileInUploadcare = $this->api->file()->fileInfo($this->file->uuid);\n        } catch (HttpException $e) {\n            throw new BadRequestHttpException($e->getMessage());\n        }\n    }\n\n    private function deleteFile(): void\n    {\n        $this->api->file()->deleteFile($this->fileInUploadcare);\n    }\n}\n","sourceCodeStart":43,"sourceCodeEnd":70,"githubUrl":"https://github.com/monicahq/monica/blob/e08e91734170b6bbd582cb578532c3948196124e/app/Domains/Contact/ManageDocuments/Listeners/DeleteFileInStorage.php#L43-L70","documentation":"DeleteFileInStorage is the Laravel listener on the FileDeleted event (contact documents/photos). It first asserts the Uploadcare keys are configured (else EnvVariablesNotSetException), then resolves the file on Uploadcare via Api->file()->fileInfo($file->uuid) before deleting it. Any Uploadcare HttpException from that lookup (404 unknown file, 401/403 bad keys, 429 rate limit) is rethrown as Symfony BadRequestHttpException, which renders as HTTP 400.","triggerScenarios":"Deleting a Contact document whose uuid no longer exists in the Uploadcare project (already deleted from the dashboard), using public/private keys from a different Uploadcare project than the one storing the files, or invalid/rotated keys so fileInfo() fails with 401/403.","commonSituations":"Env keys changed after files were uploaded (old files belong to the previous project), file removed out-of-band in the Uploadcare dashboard, or the files table's uuid out of sync with Uploadcare.","solutions":["Check services.uploadcare.public_key / private_key in .env belong to the same Uploadcare project that received the uploads","Verify the uuid exists: GET https://api.uploadcare.com/files/{uuid}/ with the same keys","Make deletion idempotent: treat 404 'file not found' as success and skip the remote delete","If keys and file are fine, read the wrapped message for 429/5xx and retry after a backoff"],"exampleFix":"// before\ntry {\n    $this->fileInUploadcare = $this->api->file()->fileInfo($this->file->uuid);\n} catch (HttpException $e) {\n    throw new BadRequestHttpException($e->getMessage());\n}\n\n// after\ntry {\n    $this->fileInUploadcare = $this->api->file()->fileInfo($this->file->uuid);\n} catch (HttpException $e) {\n    if ($e->getStatusCode() === 404) {\n        return; // already gone on Uploadcare, nothing to delete\n    }\n    throw new BadRequestHttpException($e->getMessage());\n}","handlingStrategy":"try-catch","validationCode":"// Preflight: confirm the file still exists on Uploadcare before triggering deletion\nuse Illuminate\\Support\\Facades\\Http;\n\n$response = Http::withHeaders([\n    'Accept' => 'application/vnd.uploadcare-v0.7+json',\n])->withToken($signedToken)->get(\"https://api.uploadcare.com/files/{$file->uuid}/\");\n\nif ($response->status() === 404) {\n    // already deleted upstream: drop the local row without calling the listener\n}","typeGuard":null,"tryCatchPattern":"use Symfony\\Component\\HttpKernel\\Exception\\HttpException;\n\ntry {\n    $this->api->file()->fileInfo($file->uuid);\n} catch (HttpException $e) {\n    if ($e->getStatusCode() === 404) {\n        return; // idempotent delete: file already gone\n    }\n    if ($e->getStatusCode() === 429 || $e->getStatusCode() >= 500) {\n        // transient: retry with backoff, or queue the deletion for later\n    }\n    throw $e;\n}","preventionTips":["Treat remote 404 as success so document deletion stays idempotent","Keep UPLOADCARE_PUBLIC_KEY/UPLOADCARE_PRIVATE_KEY stable per environment; changing projects orphans old files","Never delete files out-of-band in the Uploadcare dashboard without removing the Monica rows","Log the uuid and status code when the listener fails so orphaned files are traceable"],"tags":["uploadcare","file-storage","listener","monica"],"backgroundTag":"uploadcare-api-error","analyzedSha":"e08e91734170b6bbd582cb578532c3948196124e","analyzedAt":"2026-08-17T01:36:49.014Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}