monicahq/monica · error · BadRequestHttpException

$e->getMessage()

Error message

$e->getMessage()

What it means

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.

Source

Thrown at app/Domains/Contact/ManageDocuments/Listeners/DeleteFileInStorage.php:61

    {
        if (is_null(config('services.uploadcare.private_key'))) {
            throw new EnvVariablesNotSetException;
        }

        if (is_null(config('services.uploadcare.public_key'))) {
            throw new EnvVariablesNotSetException;
        }
    }

    private function getFileFromUploadcare(): void
    {
        $configuration = Configuration::create(config('services.uploadcare.public_key'), config('services.uploadcare.private_key'));
        $this->api = new Api($configuration);

        try {
            $this->fileInUploadcare = $this->api->file()->fileInfo($this->file->uuid);
        } catch (HttpException $e) {
            throw new BadRequestHttpException($e->getMessage());
        }
    }

    private function deleteFile(): void
    {
        $this->api->file()->deleteFile($this->fileInUploadcare);
    }
}

View on GitHub (pinned to e08e917341)

Solutions

  1. Check services.uploadcare.public_key / private_key in .env belong to the same Uploadcare project that received the uploads
  2. Verify the uuid exists: GET https://api.uploadcare.com/files/{uuid}/ with the same keys
  3. Make deletion idempotent: treat 404 'file not found' as success and skip the remote delete
  4. If keys and file are fine, read the wrapped message for 429/5xx and retry after a backoff

Example fix

// before
try {
    $this->fileInUploadcare = $this->api->file()->fileInfo($this->file->uuid);
} catch (HttpException $e) {
    throw new BadRequestHttpException($e->getMessage());
}

// after
try {
    $this->fileInUploadcare = $this->api->file()->fileInfo($this->file->uuid);
} catch (HttpException $e) {
    if ($e->getStatusCode() === 404) {
        return; // already gone on Uploadcare, nothing to delete
    }
    throw new BadRequestHttpException($e->getMessage());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Preflight: confirm the file still exists on Uploadcare before triggering deletion
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'Accept' => 'application/vnd.uploadcare-v0.7+json',
])->withToken($signedToken)->get("https://api.uploadcare.com/files/{$file->uuid}/");

if ($response->status() === 404) {
    // already deleted upstream: drop the local row without calling the listener
}

Try / catch

use Symfony\Component\HttpKernel\Exception\HttpException;

try {
    $this->api->file()->fileInfo($file->uuid);
} catch (HttpException $e) {
    if ($e->getStatusCode() === 404) {
        return; // idempotent delete: file already gone
    }
    if ($e->getStatusCode() === 429 || $e->getStatusCode() >= 500) {
        // transient: retry with backoff, or queue the deletion for later
    }
    throw $e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of monicahq/monica@e08e917341 (2026-08-17). Data as JSON: /api/errors/d0f9fe4f56553643. Report an issue: GitHub.