Skip to content

Error codes

Rolink errors carry a stable, machine-readable code. Branch on the code, never on the message, which may change.

  • Over HTTP, the API answers with an error status and a JSON body:

    { "error": { "code": "amount_over_limit", "message": "Amount 5 exceeds the per-transaction limit of 0.1" } }
  • In Luau, SDK methods raise a string, and Rolink.errorCode extracts the code:

    local ok, err = pcall(rolink.TransferSol, rolink, params)
    -- err = "[Rolink] amount_over_limit: Amount 5 exceeds the per-transaction limit of 0.1 (HTTP 403)"
    print(Rolink.errorCode(err)) -- "amount_over_limit"

For symptoms and step-by-step fixes, see Troubleshooting.

The SDK tries a request up to 3 times, 0.5 and then 1.5 seconds apart, when:

  • there was no HTTP answer (network_error)
  • the status is 5xx, which includes internal, rpc_error and wallet_unavailable
  • the status is 429, except recipient_rate_limited, player_rate_limited, hourly_budget_exceeded, daily_quota_exceeded and daily_tx_quota_exceeded, which are raised at once because an hourly or daily cap does not lift in a second. The per-minute rate_limited is retried.

Every other code is raised on the first answer. See HTTP requests and retries.

These can come from any /v1 route, and the last four from any route.

CodeHTTPMeaningFix
unauthorized401The x-api-key header is missing, malformed, revoked, or belongs to a deleted project.Check that the Roblox Secret holds the whole of an active key from the project’s API keys page, and that Rolink.new gets it as apiKey.
rate_limited429The project made more than 600 requests in the current minute.Cache and batch reads. Retried by the SDK.
daily_quota_exceeded429The project used its 100,000 requests for the current UTC day.Wait until 00:00 UTC, and find what calls so often in the dashboard’s usage view. Not retried by the SDK.
invalid_json400The body is not valid JSON, or is missing on a route that needs one.Send a JSON body. The SDK always does.
invalid_request400The body does not match the route’s schema. The message lists each field and problem.Read the message, such as amount: Invalid input: expected string, received number.
not_found404No such route, or GET /v1/tx/:idempotencyKey has no request with that key in this project.Check the path. For a key, check its spelling and URL encoding.
internal500An unexpected error. Most often the Solana RPC failed during a read.Retry. If it lasts, contact Rolink support.
CodeHTTPMeaningFix
invalid_user_id400A user id is not a positive integer. From GET /v1/players/:userId/wallet.Pass player.UserId.
invalid_address400A value that must be a Solana address is not one: the wallet in a read path, mint or collection, to.address, a transaction’s mint, or the address in a linking challenge.Check the base58 address. A numeric string passed as to is treated as an address, so pass a UserId as a number.
invalid_mint400The mint does not exist on the project’s cluster, or the account is not a token mint. From token reads and token transactions.Check the mint address, and that it lives on the project’s cluster (devnet or mainnet).
invalid_signature400From GET /v1/signatures/:signature: not a transaction signature. From the linking page’s verify step: the signature is malformed or does not match the wallet.Pass a base58 transaction signature. For linking, sign the exact message with the wallet named in the challenge.

Listed in the order Rolink checks them. See Order of checks. A request refused with any of these codes, except wallet_unavailable and rpc_error, does not reserve its idempotency key.

CodeHTTPMeaningFix
wallet_not_configured409The project has no managed wallet yet.Create it on the project’s Overview page in the dashboard, then fund it.
idempotency_conflict409The key was already used in this project for a different request. Different means a different kind, recipient, mint or amount, as sent. { "userId": 42 } and that player’s address count as different, and so do "1" and "1.0".Send the original request again unchanged, or use a new key for a new payment.
wallet_not_linked404to is a UserId with no wallet linked to this project.Ask the player to link a wallet first, or send to an address.
sol_transfers_disabled403The policy has no Max SOL per transfer.Set it under Transactions in the dashboard.
mint_not_allowed403The mint is not under Allowed mints in the policy.Add the mint with its maximum per transaction.
invalid_amount400The amount is zero, or has more decimals than the asset allows (9 for SOL, the mint’s decimals for tokens).Send an amount greater than zero, rounded to the asset’s decimals.
amount_over_limit403The amount is above Max SOL per transfer, or above the mint’s maximum per transaction.Send less, or raise the limit.
invalid_limit_config422A stored limit or budget cannot be represented with the asset’s decimals, for example 0.5 on a 0-decimal mint. The dashboard normally refuses such values when you save.Open the policy in the dashboard and save it again.
daily_tx_quota_exceeded429The project already has 1,000 transactions in the last 24 hours. Failed ones do not count.Wait, then retry with the same key. Not retried by the SDK.
recipient_rate_limited429The recipient wallet already received the policy’s Per recipient wallet maximum in the last hour. Failed ones do not count.Wait, or raise the limit. Not retried by the SDK.
player_rate_limited429The recipient player already received the policy’s Per player maximum in the last hour, across all their wallets.Wait, or raise the limit. Not retried by the SDK.
hourly_budget_exceeded429This amount would take the asset past its hourly budget, across everyone.Wait for the window to roll, or raise the budget if the spend is expected. Not retried by the SDK.
wallet_unavailable502The managed wallet could not sign the transaction. The attempt is recorded as failed and nothing was sent.Send again with the same key. Retried by the SDK.
rpc_error502The Solana RPC could not be reached to build the transaction. The attempt is recorded as failed and nothing was sent.Send again with the same key. Retried by the SDK.

These come from the routes behind each project’s linking page. Game servers never see them.

CodeHTTPMeaningFix
project_not_found404No active project has the slug in the URL. The project may have been deleted.Use the URL from GetProject().linkUrl or the dashboard’s Overview.
bad_origin403A POST to /auth/logout, /auth/wallet/*, /api/link/* or a dashboard form had no Origin header, or one that is not the API’s origin.Open the linking page or the dashboard directly, not through a proxy, translation service or embedded view.
not_signed_in401No valid player session for this project. A session lasts one hour and only counts on the linking page of the game it was opened for.Sign in with Roblox again on this game’s linking page. If it happens right after signing in, the browser is blocking cookies.
challenge_expired400The nonce is unknown, already used, more than 10 minutes old, or was issued for another project or to another player.Request a new challenge. A nonce is consumed by the first verify attempt, even a failed one.

invalid_address and invalid_signature can also come from these routes. They are described under Wallets and reads.

When Sign in with Roblox fails, Rolink does not answer with an error body. It redirects back to the linking page where the sign-in started, with ?error=<code>, and the page shows a message. An unknown code shows “Sign-in failed.” Players sign in through the game’s own Roblox OAuth app, so some of these are fixed in that app or in the project’s Settings → Roblox sign-in.

errorShown on the linking pageCauseFix
roblox_deniedRoblox sign-in was cancelled.Roblox returned an error, usually because the user declined.Nothing to fix. Start again. If every player gets it, check that the app allows the openid and profile scopes.
oauth_state_mismatchSign-in expired. Please try again.The sign-in cookie is missing or older than 10 minutes, there is no code, or state does not match. Without the cookie, Rolink can’t tell which linking page to return to, and redirects to /dashboard instead.Start again from the linking page, in one browser, with cookies allowed.
oauth_token_failedRoblox sign-in failed. Please try again.Roblox refused the code exchange, for example because the client secret saved in the project doesn’t match the app’s.Paste the app’s client secret again in the project’s settings. Otherwise try again.
oauth_userinfo_failedCouldn’t read your Roblox profile. Please try again.Roblox refused the profile request.Try again. If it lasts, contact Rolink support.
oauth_not_configuredThis game hasn’t set up Roblox sign-in yet.The project has no Roblox OAuth app saved: its client ID or client secret is missing. The linking page shows the same message without a redirect.Save the app’s client ID and secret in the project’s Settings → Roblox sign-in.

These come from /auth/wallet/challenge and /auth/wallet/verify, which the dashboard’s sign-in page calls. The page shows the message.

CodeHTTPMeaningFix
bad_origin403The request’s Origin header is not the API’s origin.Open the dashboard directly.
invalid_address400The wallet’s address is not a Solana address.Choose another wallet.
challenge_expired400No valid pending sign-in: the sign-in cookie is missing, older than 10 minutes, or was already used.Choose your wallet again and sign within 10 minutes, with cookies allowed.
invalid_signature400The signature is not 64 bytes (Malformed signature), or doesn’t match the wallet (The signature does not match this wallet).Sign with the account the wallet connected.

These come from dashboard actions. The dashboard shows the message on the page, next to the form or as an error page, rather than the code; the codes are listed here for reference.

CodeHTTPMeaningFix
invalid_settings400A setting, the policy or a watch list didn’t pass validation. The message lists each field and problem.Fix the values named in the message. The rules are in Limits and quotas.
project_not_found404The project doesn’t exist, was deleted, or belongs to another developer.Sign in with the wallet that created it.
wallets_unavailable503Create wallet was refused because managed wallets aren’t turned on for the hosted API yet: “Managed wallets aren’t set up on this server yet, so transactions are off.” Linking, reads, live events and the rest of the dashboard work.Try again later. Until the project has a wallet, transaction calls answer wallet_not_configured.
project_limit_reached403You already have 5 projects, the beta’s limit per developer.Delete a project you no longer use.
key_limit_reached403The project already has 10 active API keys.Revoke a key before creating another.
key_not_found404The key to revoke is already revoked, or isn’t in this project.Reload the API keys page.

invalid_name (400) and an invalid_user_id from /auth/dev come from a development-only sign-in route that does not exist on the hosted API.

These are raised by the SDK itself, not returned by the API.

CodeRaised asMeaningFix
network_error[Rolink] network_error: <HttpService error>HttpService:RequestAsync got no HTTP answer on the last of its 3 attempts.Check that HTTP requests are enabled for the experience and that the Secret’s domain is the Rolink API’s host. Leave apiUrl unset.
http_<status>[Rolink] http_502: <status message> (HTTP 502)An HTTP error without a Rolink error body, typically from something between your server and the API. 5xx and 429 are retried first.Retry later. Check that apiUrl is unset.

Some mistakes are caught before any request is sent. They raise a plain message without a code, so Rolink.errorCode returns nil:

MessageCause
[Rolink] Rolink runs on the server only; never expose your project API key to clientsRolink.new was called on a client.
[Rolink] Rolink.new expects a config tableconfig is not a table.
[Rolink] config.apiKey is required (use HttpService:GetSecret)apiKey is nil.
[Rolink] config.apiUrl must be a stringapiUrl is set to something other than a string.
[Rolink] `to` must be a UserId (number) or a Solana address (string)to is neither a number nor a string.
[Rolink] amount must be a positive number or stringamount is a negative number, NaN, or another type.