{"record":{"id":"a713362a3bcedabc","repo":"flarum/framework","slug":"this-erasure-request-has-already-been-processed","errorCode":null,"errorMessage":"This erasure request has already been processed.","messagePattern":"This erasure request has already been processed\\.","errorType":"validation","errorClass":"ValidationException","httpStatus":422,"severity":"warning","filePath":"extensions/gdpr/src/Http/Controller/ConfirmErasureController.php","lineNumber":51,"sourceCode":"    {\n        $actor = RequestUtil::getActor($request);\n        $token = Arr::get($request->getQueryParams(), 'token');\n\n        /** @var ErasureRequest $erasureRequest */\n        $erasureRequest = ErasureRequest::query()\n            ->with('user')\n            ->where('verification_token', $token)\n            ->firstOrFail();\n\n        /**\n         * @TODO: the token is enough to confirm the erasure request. We should not require the user to be logged in.\n         */\n        if ($erasureRequest->user->isNot($actor) && ! $actor->isGuest()) {\n            throw new ValidationException(['user' => 'Erase requests cannot be confirmed by different users.']);\n        }\n\n        if (in_array($erasureRequest->status, [ErasureRequest::STATUS_PROCESSED, ErasureRequest::STATUS_MANUAL])) {\n            throw new ValidationException(['request' => 'This erasure request has already been processed.']);\n        }\n\n        $ip = $request->getAttribute('ipAddress');\n\n        $erasureRequest->user_confirmed_at = Carbon::now();\n        $erasureRequest->status = ErasureRequest::STATUS_USER_CONFIRMED;\n        $erasureRequest->cancelled_at = null;\n        $erasureRequest->verification_token = null;\n        $erasureRequest->confirmation_ip = $ip;\n        $erasureRequest->save();\n\n        // Attribute to the request's owner: confirmation may arrive via the\n        // emailed token while logged out, so $actor can be a guest.\n        $this->events->dispatch(new ErasureConfirmed($erasureRequest->user, $erasureRequest));\n\n        return new RedirectResponse($this->url->to('forum')->base().'?erasureRequestConfirmed=1');\n    }\n}","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/flarum/framework/blob/4b939f685389bfe8a380e9e28ddf305a1c66950c/extensions/gdpr/src/Http/Controller/ConfirmErasureController.php#L33-L69","documentation":"ConfirmErasureController rejects confirmation if the erasure request status is already STATUS_PROCESSED or STATUS_MANUAL, throwing this ValidationException — the request has reached a terminal state and cannot be confirmed again.","triggerScenarios":"Clicking the erasure confirmation link (or re-submitting it) after the request was already fully processed (STATUS_PROCESSED) or handled manually (STATUS_MANUAL).","commonSituations":"Double-clicking the emailed link; re-visiting an old confirmation email after the request was completed; an admin already processed the request in the backend.","solutions":["Do nothing further — the erasure request is already completed; no action is needed.","If a new erasure is required, submit a fresh erasure request.","Check erasure_requests.status in the database to confirm the terminal state before retrying."],"exampleFix":"null","handlingStrategy":"validation","validationCode":"if (in_array($erasureRequest->status, [ErasureRequest::STATUS_PROCESSED, ErasureRequest::STATUS_MANUAL])) { /* skip confirmation; show 'already processed' */ }","typeGuard":null,"tryCatchPattern":"try { $http->get($confirmUrl); } catch (ValidationException $e) { if (isset($e->getErrors()['request'])) { /* treat as no-op / show status */ } }","preventionTips":["Make confirmation idempotent in your client (track processed state)","Purge old confirmation emails after processing","Check status via API before re-confirming"],"tags":["state","gdpr","idempotency"],"backgroundTag":"invalid-state-transition","analyzedSha":"4b939f685389bfe8a380e9e28ddf305a1c66950c","analyzedAt":"2026-09-15T18:09:20.879Z","contentChangedAt":"2026-09-15T18:09:20.879Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}