coollabsio/coolify · error · RuntimeException
No Vultr token found for the server team.
Error message
No Vultr token found for the server team.
What it means
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.
Source
Thrown at app/Actions/Server/DeleteServer.php:125
{
try {
$token = null;
if ($cloudProviderTokenId) {
$token = CloudProviderToken::where('id', $cloudProviderTokenId)
->where('team_id', $teamId)
->where('provider', 'vultr')
->first();
}
if (! $token) {
$token = CloudProviderToken::where('team_id', $teamId)
->where('provider', 'vultr')
->first();
}
if (! $token) {
throw new \RuntimeException('No Vultr token found for the server team.');
}
$vultrService = new VultrService($token->token);
$vultrService->deleteInstance($vultrInstanceId);
logger()->debug('Deleted server from Vultr', [
'vultr_instance_id' => $vultrInstanceId,
'team_id' => $teamId,
]);
} catch (\Throwable $e) {
logger()->error('Failed to delete server from Vultr', [
'error' => $e->getMessage(),
'vultr_instance_id' => $vultrInstanceId,
'team_id' => $teamId,
]);
throw $e;
}View on GitHub (pinned to 70b9acc424)
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.
Example fix
// before
DeleteServer::run($serverId, deleteFromVultr: true, vultrInstanceId: $server->vultr_instance_id);
// throws RuntimeException when team has no Vultr token
// after
$token = CloudProviderToken::where('team_id', $server->team_id)
->where('provider', 'vultr')
->first();
if ($token) {
DeleteServer::run($serverId, deleteFromVultr: true,
vultrInstanceId: $server->vultr_instance_id,
cloudProviderTokenId: $token->id);
} else {
DeleteServer::run($serverId); // skip provider deletion, or surface a warning
} Defensive patterns
Strategy: validation
Validate before calling
$hasVultrToken = \App\Models\CloudProviderToken::query()
->where('team_id', $server->team_id)
->where('provider', 'vultr')
->exists();
if (! $hasVultrToken) {
// surface a config error to the user instead of dispatching a doomed deletion
return back()->withErrors(['server' => 'Add a Vultr token for this team before deleting the server from Vultr.']);
}
// safe: fallback lookup in DeleteServer will find the token
\App\Actions\Server\DeleteServer::run($server->id, deleteFromVultr: true, vultrInstanceId: $server->vultr_instance_id); Type guard
function teamHasVultrToken(int $teamId): bool
{
return \App\Models\CloudProviderToken::query()
->where('team_id', $teamId)
->where('provider', 'vultr')
->exists();
} Try / catch
try {
DeleteServer::run($serverId, deleteFromVultr: true, vultrInstanceId: $instanceId);
} catch (\RuntimeException $e) {
if (str_contains($e->getMessage(), 'No Vultr token found')) {
// keep the server row, notify the team to re-link a token, do not retry blindly
report($e);
} else {
throw $e; // provider API failure — different handling
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- No DigitalOcean token found for the server team.
- You do not have permission to create tokens with root permis
- You do not have permission to create tokens with write permi
- You do not have permission to create tokens with deploy perm
- You do not have permission to create tokens with read:sensit
AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17).
Data as JSON: /api/errors/08a8c91be2338592.
Report an issue: GitHub.