{"record":{"id":"08a8c91be2338592","repo":"coollabsio/coolify","slug":"no-vultr-token-found-for-the-server-team","errorCode":null,"errorMessage":"No Vultr token found for the server team.","messagePattern":"No Vultr token found for the server team\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"app/Actions/Server/DeleteServer.php","lineNumber":125,"sourceCode":"    {\n        try {\n            $token = null;\n\n            if ($cloudProviderTokenId) {\n                $token = CloudProviderToken::where('id', $cloudProviderTokenId)\n                    ->where('team_id', $teamId)\n                    ->where('provider', 'vultr')\n                    ->first();\n            }\n\n            if (! $token) {\n                $token = CloudProviderToken::where('team_id', $teamId)\n                    ->where('provider', 'vultr')\n                    ->first();\n            }\n\n            if (! $token) {\n                throw new \\RuntimeException('No Vultr token found for the server team.');\n            }\n\n            $vultrService = new VultrService($token->token);\n            $vultrService->deleteInstance($vultrInstanceId);\n\n            logger()->debug('Deleted server from Vultr', [\n                'vultr_instance_id' => $vultrInstanceId,\n                'team_id' => $teamId,\n            ]);\n        } catch (\\Throwable $e) {\n            logger()->error('Failed to delete server from Vultr', [\n                'error' => $e->getMessage(),\n                'vultr_instance_id' => $vultrInstanceId,\n                'team_id' => $teamId,\n            ]);\n\n            throw $e;\n        }","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/coollabsio/coolify/blob/70b9acc42467278373e00de77abb40684e25b395/app/Actions/Server/DeleteServer.php#L107-L143","documentation":"Thrown by DeleteServer::deleteFromVultrById() when a deletion is requested with deleteFromVultr=true but no CloudProviderToken row with provider='vultr' exists for the server's team. The action first tries the explicitly passed cloudProviderTokenId, then falls back to any Vultr token owned by the team; if neither lookup matches, it throws RuntimeException. Unlike the Hetzner path (which silently returns), the catch block logs and re-throws, aborting the entire DeleteServer action — the server row is not deleted from Coolify.","triggerScenarios":"Calling DeleteServer::run with deleteFromVultr=true (or deleting a Server that has vultr_instance_id with provider deletion enabled) while: the team's Vultr token row was deleted, cloudProviderTokenId points at a non-Vultr token or a token of another team, or the server's team_id no longer matches the team owning the token.","commonSituations":"Vultr API token removed or rotated after the server was created; server moved between teams; automation dispatching provider deletion without passing cloudProviderTokenId; token deleted as part of cleanup while Vultr-linked servers still exist.","solutions":["Re-add a Vultr provider token for the server's team so the fallback lookup (CloudProviderToken where team_id=server team, provider='vultr') succeeds.","Pass an explicit cloudProviderTokenId of a valid Vultr token when dispatching DeleteServer.","If the Vultr instance was already destroyed manually, delete the server without deleteFromVultr so no token lookup happens.","If calling programmatically and you want best-effort deletion, catch the RuntimeException like the Hetzner path does (log + notify) instead of letting it abort."],"exampleFix":"// before\nDeleteServer::run($serverId, deleteFromVultr: true, vultrInstanceId: $server->vultr_instance_id);\n// throws RuntimeException when team has no Vultr token\n\n// after\n$token = CloudProviderToken::where('team_id', $server->team_id)\n    ->where('provider', 'vultr')\n    ->first();\nif ($token) {\n    DeleteServer::run($serverId, deleteFromVultr: true,\n        vultrInstanceId: $server->vultr_instance_id,\n        cloudProviderTokenId: $token->id);\n} else {\n    DeleteServer::run($serverId); // skip provider deletion, or surface a warning\n}","handlingStrategy":"validation","validationCode":"$hasVultrToken = \\App\\Models\\CloudProviderToken::query()\n    ->where('team_id', $server->team_id)\n    ->where('provider', 'vultr')\n    ->exists();\nif (! $hasVultrToken) {\n    // surface a config error to the user instead of dispatching a doomed deletion\n    return back()->withErrors(['server' => 'Add a Vultr token for this team before deleting the server from Vultr.']);\n}\n// safe: fallback lookup in DeleteServer will find the token\n\\App\\Actions\\Server\\DeleteServer::run($server->id, deleteFromVultr: true, vultrInstanceId: $server->vultr_instance_id);","typeGuard":"function teamHasVultrToken(int $teamId): bool\n{\n    return \\App\\Models\\CloudProviderToken::query()\n        ->where('team_id', $teamId)\n        ->where('provider', 'vultr')\n        ->exists();\n}","tryCatchPattern":"try {\n    DeleteServer::run($serverId, deleteFromVultr: true, vultrInstanceId: $instanceId);\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'No Vultr token found')) {\n        // keep the server row, notify the team to re-link a token, do not retry blindly\n        report($e);\n    } else {\n        throw $e; // provider API failure — different handling\n    }\n}","preventionTips":["Keep at least one Vultr CloudProviderToken per team that owns Vultr-linked servers.","Always pass cloudProviderTokenId when dispatching provider deletions.","Guard the UI/API action with an exists() check on the token before offering 'delete from Vultr'.","Treat provider cleanup as best-effort: catch the re-thrown RuntimeException and continue Coolify-side deletion if that fits your workflow."],"tags":["vultr","cloud-provider-token","server-deletion","api-token","team"],"backgroundTag":"missing-cloud-provider-token","analyzedSha":"70b9acc42467278373e00de77abb40684e25b395","analyzedAt":"2026-08-17T01:41:01.313Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}