Skip to main content

Getting started

Errors

Status codes, the error body shape, and what to retry.


Every failure returns a JSON body. The status code tells you the category; the body tells you what to do about it.

Body shape

TODO: the error body shape is not final. It needs to match what the existing session-auth routes already return, and those currently return two different shapes depending on whether the failure came from the Zod validation middleware or from a controller. The public API should pick one.

The two shapes in play today:

JSON
{
  "success": false,
  "message": "Clan not found."
}
JSON
{
  "error": "Validation failed",
  "issues": [
    { "path": ["amount"], "message": "Expected number, received string" }
  ]
}

Status codes

CodeMeaningRetry?
400The request was malformed, or asked for something the clan type forbidsNo — fix the request
401Key missing, malformed, or revokedNo
403Key is valid but not scoped to the clan in the pathNo
404Clan, member or resource does not existNo
409Conflicts with the clan's linked Roblox groupNo — act on the group instead
422Semantically invalid — a rank that belongs to another clan, sayNo
429Rate limitedYes, after the documented delay
500Server faultYes, with backoff
503Dependency unavailable — usually Roblox Open CloudYes, with backoff

TODO: confirm this list against the real implementation. 422 in particular may collapse into 400.

Linked clan conflicts

A 409 on a Linked clan carries extra fields so a client can send the user to the Roblox group rather than only reporting a refusal:

JSON
{
  "success": false,
  "code": "ROBLOX_GROUP_REQUIRED",
  "robloxGroupId": "0000000",
  "robloxGroupUrl": "https://www.roblox.com/groups/0000000"
}

Membership on a Linked clan is owned by the Roblox group. Joins, leaves and ownership transfers all have to happen on roblox.com.

Handling errors in Luau

HttpService:RequestAsync does not throw on a non-2xx response — it returns one with Success = false. Checking only for a thrown error will silently swallow every 401 and 429 you ever get.

Luau
local HttpService = game:GetService("HttpService")

local ok, response = pcall(function()
    return HttpService:RequestAsync({
        Url = "https://API_BASE_URL/v0/clans/CLAN_ID",
        Method = "GET",
        Headers = { ["Authorization"] = "Bearer YOUR_CLAN_API_KEY" },
    })
end)

if not ok then
    -- Network failure, or HTTP requests disabled in Game Settings.
    warn("Request could not be sent:", response)
    return
end

if not response.Success then
    -- Reached the API; the API said no.
    warn("API error:", response.StatusCode, response.Body)
    return
end

local clan = HttpService:JSONDecode(response.Body)

What to retry

Retry 429, 500 and 503. Nothing else — a 4xx other than 429 will fail identically no matter how many times you send it, and retrying it just burns your rate limit.

Use exponential backoff with jitter. A Roblox game server that retries on a fixed interval will synchronise with every other server in your experience and arrive as a spike.