Error codes
When something is refused, the reason arrives as a short machine-readable code. The console turns most of them into a toast; the API returns them as JSON.
{
"detail": {
"code": "OFFER_UNAVAILABLE",
"message": "This offer is no longer available"
}
}
A few codes carry extra fields alongside code and message — DISK_TOO_LARGE returns max_disk_gb and MACHINE_DISK_EXHAUSTED returns available_disk_gb, so a client can clamp the slider and retry without a second round trip.
Those two are not interchangeable, and clamping to the wrong one loops forever. max_disk_gb is the machine's total disk; available_disk_gb is what is left after the other renters on it and a 50 GB host reserve. A machine can therefore pass DISK_TOO_LARGE and still answer MACHINE_DISK_EXHAUSTED — even an empty one, because the reserve is never sellable. Clamp to available_disk_gb when you get it.
Authentication and organization scope
| Status | Code | Cause | What to do |
|---|---|---|---|
| 401 | TOKEN_INVALID | No Authorization header, a malformed token, an unknown or revoked API key, or an API key sent to an account endpoint | Send a valid bearer token. Account endpoints such as /v1/me and /v1/ssh-keys need a signed-in session, not an shk_ key |
| 401 | TOKEN_EXPIRED | The session token is past its expiry | Sign in again. For unattended scripts, use an API key instead |
| 403 | NOT_A_MEMBER | X-Org-Id names an organization you do not belong to | Drop the header to use your personal organization, or ask an admin for an invite |
| 403 | ORG_MISMATCH | X-Org-Id does not match the organization the API key was created in | Remove the header, or use a key belonging to that organization |
| 403 | ADMIN_REQUIRED | An admin-only action — checkout, invites, member management, team API keys — attempted as a member or with an API key | Ask an admin, or repeat the action from a signed-in admin session |
| 400 | INVALID_ORG_ID | X-Org-Id is not a valid id | Copy the id from GET /v1/orgs |
Deploying
| Status | Code | Cause | What to do |
|---|---|---|---|
| 402 | INSUFFICIENT_BALANCE | Your balance does not cover one hour of the instance — GPU plus disk — when you deploy or start it. Being above zero is not enough | Add credits. Only an admin can top up |
| 409 | OFFER_UNAVAILABLE | The last free unit of that shape was taken between the moment you opened it and the moment you deployed | Go back to the marketplace and pick another offer. The count on a card is a snapshot; the deploy is what decides |
| 404 | OFFER_NOT_FOUND | GET /v1/offers/{id} was given an id that does not exist or has been withdrawn from the catalog. A deploy against an unknown offer returns OFFER_UNAVAILABLE instead | Re-list offers and use a current id |
| 422 | DISK_TOO_LARGE | The requested disk_gb is more than the machine has in total. The response includes max_disk_gb | Request no more than max_disk_gb, or choose a machine with a larger disk |
| 409 | MACHINE_IN_MAINTENANCE | The machine is booked to go offline too soon to start an instance on it. The response includes next_maintenance | Pick another machine, or come back after the window. A window further out does not block a deploy — it is shown on the offer instead |
| 409 | MACHINE_DISK_EXHAUSTED | The machine's disk is already committed to other renters. It carries several at once, and disk is sold against what is left, not against the total — so this can follow a disk_gb that cleared DISK_TOO_LARGE. The response includes available_disk_gb and requested_disk_gb | Retry at or below available_disk_gb, or pick another machine. available_disk_gb can be 0 |
| 422 | OFFER_NOT_VM_CAPABLE | A vm launch-mode template was aimed at a machine that cannot run virtual machines | Deploy the template onto a VM-capable offer, or pick a template in another launch mode |
| 404 | TEMPLATE_NOT_FOUND | The template id does not exist, or the template has been deleted | Use a template from the gallery, or a hash_id you were given |
| 404 | SSH_KEY_NOT_FOUND | The ssh_key_id is not one of your keys | List GET /v1/ssh-keys and use an id from your own account. Keys belong to a user, not to an organization |
Instance actions
| Status | Code | Cause | What to do |
|---|---|---|---|
| 404 | INSTANCE_NOT_FOUND | The instance id does not exist in the organization the request is scoped to | Check the id, and check you are in the right organization |
| 409 | INVALID_STATE | The action is not legal from the instance's current state — stopping something already stopping, destroying something mid-creation | Wait for the instance to reach running or stopped, then retry. See Instance states |
Templates
| Status | Code | Cause | What to do |
|---|---|---|---|
| 403 | TEMPLATE_IMMUTABLE | You tried to edit or delete a system template curated by Superheat | Duplicate it first, then edit your copy |
| 409 | TEMPLATE_NAME_TAKEN | Your organization already has a template with that name, ignoring case | Choose a different name |
| 404 | TEMPLATE_NOT_FOUND | The id or hash_id does not resolve to a template you can see | Confirm the share link is complete and current — a hash_id changes when the launch recipe changes |
| 422 | INVALID_TAB | The tab query parameter on the template listing is not one of the gallery tabs | Use a supported tab value |
SSH keys
| Status | Code | Cause | What to do |
|---|---|---|---|
| 422 | INVALID_SSH_KEY | The value is not an OpenSSH public key line, the key material is not valid base64, or the type is not supported | Paste the whole contents of the .pub file, one line, starting with the key type. See Supported key types |
| 409 | DUPLICATE_SSH_KEY | You already added a key with the same fingerprint | Use the key you already have, or delete the old entry first |
| 404 | SSH_KEY_NOT_FOUND | The key id is not yours | Keys are per user. Another member's key is never visible to you |
Team and invites
| Status | Code | Cause | What to do |
|---|---|---|---|
| 400 | PERSONAL_ORG | You tried to invite someone into a personal organization | Create a real organization first, then invite from there |
| 404 | INVITE_NOT_FOUND | The invite token is wrong or the invite was withdrawn | Ask an admin for a fresh link |
| 410 | INVITE_USED | The invite has already been accepted | Ask for a new invite link |
| 410 | INVITE_EXPIRED | The invite is past its expiry | Ask for a new invite link |
| 404 | MEMBER_NOT_FOUND | The user is not a member of this organization | Refresh the team page — they may have been removed already |
| 400 | LAST_ADMIN | Removing that member, or demoting them, would leave the organization with no admin | Promote another member to admin first |
Credits
| Status | Code | Cause | What to do |
|---|---|---|---|
| 422 | INVALID_AMOUNT | A custom top-up amount is missing or outside the accepted range | Pick a $10, $25 or $100 pack, or a custom amount between $5 and $1,000 |
| 403 | ADMIN_REQUIRED | Checkout was attempted by a member or with an API key | Ask an organization admin to add credits |
API keys
| Status | Code | Cause | What to do |
|---|---|---|---|
| 404 | API_KEY_NOT_FOUND | The key id does not exist in this organization | Re-list your keys and use a current id |
| 403 | ADMIN_REQUIRED | An API key tried to create or revoke another API key, or a member tried to manage a team-level key | Create and revoke keys from a signed-in session |
Requests that fail validation
A request whose body or query string does not match the schema is rejected before it reaches any of the checks above. It returns 422 with a list of field errors under detail instead of a code:
{
"detail": [
{
"type": "less_than_equal",
"loc": ["body", "disk_gb"],
"msg": "Input should be less than or equal to 20000"
}
]
}
The loc entry names the offending field. The bounds worth remembering are disk_gb between 10 and 20,000, a label of at most 64 characters, and tail on the logs endpoint between 1 and 1,000.