What does an error look like?
Every error uses the same envelope, described in the API introduction. The HTTP status tells you the category,code is a stable string safe to branch on, and details — when present — carries the numbers you need to act (a limit, a retry delay, the missing scope).
{
"code": "ERROR_CODE",
"message": "Optional descriptive message",
"details": { "retry_after": 30 }
}
message is for humans and can change; never branch your code on it. For deploy and runtime problems that don’t come from the API itself (crash loops, missing dependencies, OOMKilled), see Common Errors — this page only covers the stable code values the API answers with.
VALIDATION_ERROR (400) covers request-shape problems across every route — a required field
missing, the wrong type, an out-of-range value. details.path names the offending field, e.g.
"memory" or "user.name".Auth & API keys
| Status | Code | Meaning & fix |
|---|---|---|
| 401 | AUTH_TOKEN_MISSING | No Authorization header was sent. Add Authorization: Bearer <key>. |
| 401 | API_KEY_INVALID | The key doesn’t exist or its secret doesn’t match. Check the key value, or create a new one in the dashboard. |
| 401 | SESSION_REVOKED | The dashboard session behind the token expired or was revoked (signed out, or ended from the sessions list). Sign in again. |
| 401 | USER_NOT_FOUND | The authenticated user no longer exists (rare — usually a deleted account). |
| 403 | API_KEY_IP_DENIED | The request’s IP isn’t in the key’s allowed_ips. Add the caller’s IP/CIDR to the key, or clear the list to allow any IP. |
| 403 | API_KEY_MANAGED | The key was created by the platform for a stack. It cannot be edited; it is revoked together with the stack. |
| 403 | API_KEY_SCOPE_DENIED | The key is missing the scope the route requires (details.required), or the route is dashboard-session only. See Scopes. |
| 403 | WEBSITE_ONLY | The route only accepts a dashboard session. API keys normally get API_KEY_SCOPE_DENIED first on these routes; WEBSITE_ONLY is the second check behind it. No scope unlocks the action — use the dashboard. |
| 403 | ACCESS_DENIED | The resource exists but your account can’t act on it. Confirm you own the resource or have the right workspace permission. |
| 403 | ACCOUNT_BANNED | The account was banned. Contact support. |
| 403 | APP_BANNED | The application was banned. Contact support. |
| 403 | DATABASE_BANNED | The database was banned. Contact support. |
| 403 | APP_SHIELD_COOLDOWN | Vertra Shield paused the application after abusive traffic; details names the reason and cooldown end. Wait for the pause to end and fix the traffic source; start, restart, deploy, configuration and deletion stay blocked until then. See Common Errors. |
| 404 | USER_LIMITS_NOT_FOUND | The account’s plan limits couldn’t be resolved. Contact support. |
Plans & billing
| Status | Code | Meaning & fix |
|---|---|---|
| 400 | INVALID_PLAN / INVALID_PLAN_NAME | The plan slug isn’t recognized. Use one from Plans and limits. |
| 400 | MISSING_PLAN_OR_MONTHS | plan or months is missing from the order body. |
| 400 | INVALID_MONTHS | months isn’t 1, 3, 6 or 12. |
| 400 | NOT_AN_UPGRADE | The target plan isn’t a memory upgrade over the current one. |
| 400 | PLAN_NOT_FOR_SALE | The plan is no longer sold because it isn’t part of the current grid. Pick a plan from List Plans. |
| 400 | COUPON_INVALID | No active coupon has this code. |
| 400 | COUPON_INACTIVE / COUPON_NOT_STARTED / COUPON_EXPIRED | The coupon is switched off, hasn’t started or has expired. |
| 400 | COUPON_EXHAUSTED / COUPON_USER_LIMIT | The coupon reached its total number of uses, or the account already used it as many times as allowed. |
| 400 | COUPON_AUDIENCE / COUPON_PLAN / COUPON_DURATION | The coupon isn’t available to this account, or doesn’t apply to this plan or billing period. |
| 400 | PLAN_MISMATCH_FOR_RENEWAL | A renewal order’s plan doesn’t match the resource’s current plan. |
| 400 | BILLING_DETAILS_INCOMPLETE | The order needs complete billing details (name, address and, in Brazil, CPF/CNPJ). Save them with PUT /v1/users/me/billing, then create the order again. |
| 400 | BILLING_DETAILS_INVALID | A billing details field failed validation; the field is in details.path. |
| 400 | TAX_ID_INVALID | The CPF or CNPJ is not valid. |
| 400 | ORDER_NOT_PAID | The order hasn’t been paid yet — wait for PIX confirmation. |
| 400 | ORDER_NOT_PAYABLE | The order isn’t in a payable state (already paid, expired, or cancelled), or a card payment of the order is still being confirmed (409). |
| 400 | COUPON_PAYMENT_METHOD | The order’s coupon doesn’t allow this payment method. Pay with a method listed in allowed_payment_methods. |
| 400 | PAYMENT_METHOD_UNAVAILABLE | Online payment is temporarily unavailable. Try again later. |
| 400 | ORDER_NOT_REFUNDED | Requested an action that requires a refunded order, but this one isn’t refunded. |
| 400 | REFUND_AMOUNT_EXCEEDS_PRICE | The requested refund is larger than the order’s price. |
| 400 | PENDING_ORDER_LIMIT_REACHED | The account already has the maximum number of unpaid orders open. Pay or let one expire first. |
| 404 | ORDER_NOT_FOUND | No order with that ID on this account. |
| 404 | INVALID_OR_EXPIRED_CODE | The redeem code doesn’t exist, was already used, or expired. |
| 403 | PLAN_NOT_ALLOWED | The current plan doesn’t allow this action (e.g. manual snapshots). Upgrade to use it. |
| 403 | PLAN_RESTRICTED_FEATURE | The feature isn’t available on the resource owner’s plan. |
| 403 | PLAN_REQUIRED | The account has no active plan, so it can’t create applications or databases, start or restart them, deploy, or publish a site. Subscribe to a plan to continue. |
| 403 | PLAN_DOES_NOT_SUPPORT_WEB_PUBLISH | The current plan tier doesn’t include web publishing. |
| 403 | PLAN_DOES_NOT_SUPPORT_CUSTOM_DOMAIN | Custom domains require Starter 2 or higher. |
| 403 | PLAN_DOES_NOT_SUPPORT_CUSTOM_SUBDOMAIN | The plan doesn’t include choosing a custom subdomain; every plan of the current grid does. See Plans and limits. |
| 403 | PLAN_DOES_NOT_SUPPORT_AUTO_ROLLBACK | Automatic rollback requires Pro or higher. Turning it off is always allowed. |
| 403 | PLAN_DOES_NOT_SUPPORT_ZERO_DOWNTIME | Forcing zero-downtime deploys (zero_downtime: "on") requires Starter 2 or higher. auto and off are always allowed. |
| 403 | PLAN_DOES_NOT_SUPPORT_ROLLBACK | The plan doesn’t include rolling back to an earlier release; every plan of the current grid does. See Deploy history and rollback limits. |
Applications
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | APP_NOT_FOUND | No application with that ID (or not owned by this account/workspace). |
| 404 | RELEASE_NOT_FOUND | No release with that ID for this application. See List Application Releases. |
| 404 | OWNER_APP_WITHOUT_LIMITS | The application’s owner has no resolvable plan limits. Contact support. |
| 409 | RELEASE_ALREADY_ACTIVE | The release is already the one the application runs, or it has the same code and there are no different environment variables to restore. Pick another release. |
| 410 | RELEASE_EXPIRED | The code of that release is no longer kept: it expired, or it is older than the number of releases your plan keeps. Pick a more recent release. |
| 400 | INVALID_CONFIG | The application configuration (build/start command, language) failed validation. |
| 400 | MEMORY_REQUIRED | memory is required and wasn’t sent, or isn’t a number. |
| 400 | MEMORY_BELOW_MINIMUM | The requested memory is below the plan’s floor; details carries the numbers. |
| 400 | MEMORY_LIMIT_EXCEEDED | The requested memory exceeds what the plan allows for this account. |
| 400 | INVALID_MEMORY_FORMAT | memory isn’t a valid integer. |
| 422 | INSUFFICIENT_MEMORY | The build ran out of memory while installing dependencies. |
| 400 | INVALID_RAM_LIMIT | The RAM value is below the minimum for the selected database engine. |
| 403 | MAX_APPS_REACHED | The plan’s application count limit was reached. Delete one or upgrade. |
| 400 | INVALID_LANGUAGE | The selected runtime language isn’t supported. |
| 400 | INVALID_DATABASE_VERSION | The database version isn’t available for the engine. Use one listed by List Database Versions. |
| 400 | INVALID_VERSION | The runtime version doesn’t exist for the application’s language. Use recommended, latest or a version listed by List Runtimes. |
| 409 | APP_BUILDING | The application is still building after creation. Start, stop, restart, file deploys, rollback, snapshots, restores and configuration changes wait for the build to end (up or down); deleting is allowed. Poll Application Details and retry. |
| 409 | CONTAINER_ALREADY_RUNNING | Start was requested on an application that’s already running. |
| 409 | OPERATION_IN_PROGRESS | Another lifecycle operation on the same resource is already running. This is contention, not a failure — wait a few seconds and retry. |
| 400 | NO_FIELDS_TO_UPDATE / NO_CHANGES_PROVIDED | The update body has nothing to change. |
| 409 | STORAGE_ABOVE_NEW_LIMIT | A config change was refused because the volume already holds more than the new disk ceiling; details carries the numbers. |
| 400 | SOURCE_REQUIRED / SOURCE_CONFLICT | Application creation needs exactly one source (file XOR snapshot_id); one is missing or both were sent. |
| 400 | INVALID_ZIP / INVALID_ZIP_STRUCTURE | The ZIP is unreadable, or its files are wrapped in an extra top-level folder — files must sit at the ZIP root. A damaged ZIP is also refused when restoring a snapshot or a release, before anything changes. |
| 413 | ARCHIVE_TOO_LARGE / ARCHIVE_TOO_MANY_ENTRIES | The ZIP holds more content than fits once decompressed, or more files than allowed. Send a smaller project. |
| 413 | DOWNLOAD_TOO_LARGE | The project has more than 300 MB of files and can’t be downloaded directly as a ZIP. Create a snapshot and download the snapshot, which has no size limit. |
| 400 | ZIP_TOO_SMALL | The uploaded ZIP is too small to be a real project. |
| 400 | ZIP_TOO_LARGE | The uploaded ZIP exceeds the 100 MB limit. |
| 400 | MAIN_FILE_NOT_FOUND | The scanner couldn’t find an entry point in the uploaded project. |
| 422 | BUILD_FAILED | The build_command exited with an error on start or restart. Check the build log and fix the command. On creation this isn’t an error: the application ends the build stopped (status: "down") with the output in its logs. |
| 422 | BUILD_TIMEOUT | The build_command ran past its 10-minute limit. Simplify the build. On creation, the application is created stopped. |
| — | BUILD_INTERRUPTED | The build didn’t finish (it was interrupted). The application stays down and this error appears in its log. Start it again or deploy again. |
| 422 | INSTALL_TIMEOUT | Dependency installation ran past its 15-minute limit. On creation, the application is created stopped. |
| 422 | INSTALL_FAILED | Dependency installation failed. Check the dependency manifest. On creation, the application is created stopped. |
| 422 | INSTALL_OUT_OF_MEMORY | Dependency installation ran out of memory. Raise the application memory. On creation, the application is created stopped. |
| 422 | INSTALL_CANCELLED | The installation was cancelled (e.g. by a new deploy superseding it). |
| 422 | INSTALL_SHIELD_BLOCKED | Vertra Shield stopped the deployment because traffic exceeded the plan allowance. On creation, the application is created stopped. |
| 429 | DEPLOY_RATE_LIMITED | The account’s hourly deploy budget or its own hourly rollback budget was reached; details.retry_after says how long to wait. See rate limits below. |
Build failure codes
When a build or install fails,GET /v1/apps/{id} returns the code in last_build_error (null after a successful build); the detail is in the application’s logs.
last_build_error | What to do |
|---|---|
INSTALL_OUT_OF_MEMORY | Raise the application memory and try again. |
BUILD_FAILED / INSTALL_FAILED | Read the logs and fix the project. |
BUILD_TIMEOUT / INSTALL_TIMEOUT | Make the build or the installation shorter. |
BUILD_INTERRUPTED | Try again. |
INSTALL_SHIELD_BLOCKED | Network protection blocked traffic during the build; reduce the traffic the build generates. |
Web Analytics
| Status | Code | Meaning & fix |
|---|---|---|
| 403 | PLAN_DOES_NOT_SUPPORT_WEB_ANALYTICS_PERFORMANCE | The plan does not include the performance details (Starter). Upgrade to Pro or higher. |
| 403 | PLAN_DOES_NOT_SUPPORT_WEB_ANALYTICS_REQUESTS | The plan does not include the request log (Starter). Upgrade to Pro or higher. |
| 400 | ANALYTICS_RANGE_EXCEEDS_PLAN | start is earlier than the plan can read. details.history_days is the limit in days and details.earliest is the earliest start accepted right now (ISO 8601). Move start forward. |
| 503 | ANALYTICS_BUSY | High-demand moment and no recent answer available for the query. Wait details.retry_after seconds and try again. |
Deploy & GitHub
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | DEPLOYMENT_NOT_FOUND | No deploy with that ID for this application. |
| 500 | DEPLOY_FAILED | The GitHub-triggered deploy failed while fetching or applying the commit. Check the Activity tab. |
| 409 | REPO_ALREADY_LINKED | That repository is already linked to another application. Unlink it first. |
| 404 | INSTALLATION_NOT_FOUND | The GitHub App installation for this account/repository wasn’t found. Reconnect GitHub. When creating a Docker image from GitHub, it also means the repository is private or doesn’t exist (or the branch doesn’t) and isn’t in your installations; public repositories outside them are built once with no link. |
| 413 | FILE_TOO_LARGE | A single file from the repository exceeds the size the GitHub file endpoint allows. |
| 502 | FETCH_ZIP_ERROR | GitHub could not be reached or was limiting requests while the repository was fetched or checked. Try again in a few minutes. |
| 400 / 403 | FORBIDDEN_PATH | The requested repository path is outside what the app is allowed to read (400 as a generic rejection, 403 when it’s specifically a forbidden path). |
Files
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | FILE_NOT_FOUND | No file at that path. |
| 404 | FILE_OR_FOLDER_NOT_FOUND | No file or folder at that path. |
| 400 | INVALID_PATH | The path is malformed or escapes the application’s file root. |
| 403 | FORBIDDEN_PATH | The path is in a directory the API won’t let you touch (e.g. reserved system paths). |
| 400 | MISSING_CONTENT | A file write was sent with no content. |
| 400 | TARGET_IS_DIRECTORY | Requested a file operation on a path that’s actually a directory. |
| 409 | FILE_MODIFIED | The file changed on disk since you last read it (optimistic-lock conflict). Re-read and retry. |
| 409 | PATH_IS_FILE | A folder in the path is an existing file (for example, writing app/index.js when app is a file). Rename or delete that file and retry. |
| 400 | NO_FILE_UPLOADED | The upload request had no file attached. |
| 413 | PAYLOAD_TOO_LARGE | The request body (upload) is larger than the limit for the route. |
| 400 | STORAGE_FULL | The application’s volume has no space left for this write. |
Blob Storage
| Status | Code | Meaning & fix |
|---|---|---|
| 403 | BLOB_PREFIX_DENIED | The API key is restricted to a Blob folder and the operation is outside it, or it is an account-level operation (rules, domains, statistics). Use a key without the restriction. |
| 404 | BLOB_OBJECT_NOT_FOUND | No object with that ID in this account (or it belongs to another account). Check the ID. |
| 409 | BLOB_OBJECT_ALREADY_EXISTS | Another object already has that path. Send overwrite: true to replace it, or choose another path. |
| 404 | BLOB_UPLOAD_NOT_FOUND | The upload doesn’t exist, belongs to another account, or was already completed or aborted. Start a new upload. |
| 410 | BLOB_UPLOAD_EXPIRED | The upload’s time limit has passed. Start a new upload. |
| 409 | BLOB_UPLOAD_INCOMPLETE | At complete, parts are missing or the stored size differs from the declared size. Upload the missing parts (see uploaded_parts) and retry. |
| 409 | BLOB_UPLOAD_LIMIT | The account has more uploads in progress than its plan allows. Complete or abort some, then retry. |
| 400 | BLOB_CHECKSUM_MISMATCH | The checksum_sha256 you sent doesn’t match the content. Compute it again from the file you are sending. |
| 413 | BLOB_FILE_TOO_LARGE | The file is above the limit that applies: 100 MB on POST /v1/objects, 10 GB per object, or a folder rule’s or upload token’s max_size. |
| 403 | STORAGE_QUOTA_EXCEEDED | Storing the file would exceed the blob quota of the plan. Delete objects or move to a plan with more storage. |
| 422 | BLOCKED_FILE_TYPE | The extension is blocked, isn’t allowed by the folder rule or upload token, or the content is an executable. |
| 403 | BLOB_NAMESPACE_SUSPENDED | Blob storage is suspended, or the plan expired. Writes are refused from the start of the 72-hour grace period (files keep being served until it ends, unless the account is suspended for abuse). Renew the plan, or contact support if the account is suspended. |
| 401 | BLOB_UPLOAD_TOKEN_INVALID | The upload token doesn’t exist, has expired or was revoked. Create a new token. |
| 401 | BLOB_UPLOAD_TOKEN_EXHAUSTED | The upload token has used all its uses. Create a new token with a higher max_uses. |
| 403 | BLOB_UPLOAD_TOKEN_NOT_ALLOWED | An upload token was sent to a route other than the upload routes. Use an API key there. |
| 403 | BLOB_UPLOAD_TOKEN_CONSTRAINT | The path, folder or another option the upload token fixes (visibility, cache, disposition, expiration or metadata) doesn’t match the request. An extension outside the token answers BLOCKED_FILE_TYPE, and a size above its max_size answers BLOB_FILE_TOO_LARGE. |
| 404 | BLOB_SHARE_NOT_FOUND | The share doesn’t exist, was revoked, or has expired. |
| 410 | BLOB_SHARE_EXHAUSTED | The share has used all its downloads. |
| 401 | BLOB_SHARE_PASSWORD_INVALID | The password is wrong. Repeated attempts from the same IP are rate limited. |
| 409 | BLOB_RULES_CONFLICT | The If-Match etag is out of date because the rules were saved after you read them. Read the rules again and retry. |
| 409 | BLOB_RULE_LIMIT | The account has more folder rules than its plan allows (5, 10 or 20). Delete a rule first. |
| 409 | BLOB_CUSTOM_DOMAIN_LIMIT | The plan includes custom domains and the account already has as many as it allows. Below Pro the answer is PLAN_RESTRICTED_FEATURE instead. |
| 409 | BLOB_CUSTOM_DOMAIN_TAKEN | The hostname is already in use. |
| 404 | BLOB_CUSTOM_DOMAIN_NOT_FOUND | The domain doesn’t exist or belongs to another account. |
VALIDATION_ERROR, PLAN_RESTRICTED_FEATURE, RATE_LIMIT_EXCEEDED (with retry_after, also used for the one-upload-per-second limit below Business), API_KEY_SCOPE_DENIED, WORKSPACE_PERMISSION_DENIED and INVALID_CUSTOM_DOMAIN_FORMAT.
Network & domains
| Status | Code | Meaning & fix |
|---|---|---|
| 400 | APP_ALREADY_PUBLISHED | The application already has a subdomain or custom domain. |
| 400 | APP_NOT_PUBLISHED | The application has no subdomain and no custom domain yet — publish it first. |
| 400 | APP_HAS_NO_SUBDOMAIN | An operation that needs an existing subdomain was called before one was set. |
| 409 | SUBDOMAIN_TAKEN | Another application already uses that subdomain. |
| 400 | INVALID_SUBDOMAIN_FORMAT | The subdomain doesn’t match the allowed character set/length. |
| 400 | SUBDOMAIN_FORBIDDEN | The requested subdomain is reserved and can’t be used. |
| 429 | SUBDOMAIN_CHANGE_RATE_LIMITED | Subdomain changes are capped at 5/minute with a 5-minute cooldown. |
| 400 | INVALID_CUSTOM_DOMAIN_FORMAT | The custom domain string isn’t a valid hostname. |
| 400 | FORBIDDEN_DOMAIN | The domain is reserved and can’t be added to an application. |
| 400 | INVALID_DOMAIN_PORT | A domain’s port isn’t an integer from 1 to 65535. |
| 400 | INVALID_PATH_PREFIX | A domain’s path_prefix must start with /, must not end with /, and may only use letters, digits and . _ ~ -. |
| 400 | INVALID_REDIRECT_TARGET | redirect_to must be another active domain of the same application that doesn’t redirect itself. |
| 403 | DOMAIN_LIMIT_REACHED | The application already has as many custom domains as the plan allows; details.limit carries the limit. The automatic www doesn’t count. |
| 403 | DOMAIN_PORT_LIMIT_REACHED | The application already serves domains on 5 ports besides 80; details.limit carries the limit. |
| 409 | DOMAIN_ALREADY_IN_USE | That custom domain is already linked to an application. |
| 404 | DOMAIN_NOT_FOUND | The application has no domain with that ID. |
| 404 | DOMAIN_CONNECT_UNAVAILABLE | Automatic DNS setup is not available yet, so every call answers this code. Create the records by hand. |
| 404 | CUSTOM_DOMAIN_RECORD_NOT_FOUND | The DNS records of the domain aren’t available; retry in a few minutes. |
| 502 | CUSTOM_DOMAIN_REGISTRATION_FAILED | The platform failed to register the domain/certificate. Retry, or contact support if it persists. |
| 403 | INVALID_HOSTNAME_FOR_APP | The hostname used for an authorized network path isn’t allowed for this application. |
| 400 | NO_AUTHORIZED_PATHS | The network configuration ended up with no valid authorized paths after validation. |
Databases
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | DATABASE_NOT_FOUND | No database with that ID (or not owned by this account/workspace). |
| 409 | DATABASE_NOT_RUNNING | The database must be started for this operation (e.g. a manual snapshot); Redis has no such requirement. |
| 500 | DATABASE_PERSISTENCE_FAILED | The database was created but couldn’t be read back. Retry, or contact support. |
| 403 | MAX_DATABASES_REACHED | The plan’s database count limit was reached. |
| 404 | CERTIFICATE_NOT_FOUND | No TLS certificate available for this database yet. |
| 429 | FORCED_SNAPSHOT_RATE_LIMIT | Too many safety snapshots (before a reset or a stack or image delete) in a short time; nothing was changed. |
| 500 | SNAPSHOT_FAILED | The safety snapshot before a reset (or before deleting a stack or an image) failed; nothing was changed. Contact support. |
Environment variables
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | ENV_NOT_FOUND | No environment variable with that key. |
| 400 | ENV_LIMIT_EXCEEDED | The application already has the maximum number of environment variables (256). |
| 400 | ENV_DUPLICATE_KEY | The batch has the same variable key more than once. |
| 400 | FORBIDDEN_KEY | The key name is reserved by the platform (e.g. PORT) and can’t be set. |
Snapshots
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | SNAPSHOT_NOT_FOUND | No snapshot with that ID, or it doesn’t belong to this account. |
| 400 | SNAPSHOT_TYPE_MISMATCH | The snapshot’s resource type (application/database) doesn’t match what was requested. |
| 400 | SNAPSHOT_ENGINE_MISMATCH | The snapshot’s database engine doesn’t match the target database. |
| 400 | TYPE_MISMATCH | Can’t restore a snapshot onto a resource of a different type. |
| 400 | SNAPSHOT_VERSION_NEWER | The snapshot was taken on a newer major version than the database being created or restored; nothing was changed. |
| 400 | SNAPSHOT_TOO_LARGE | The snapshot exceeds the size the target plan/resource allows. |
| 413 | SNAPSHOT_TOO_LARGE | Creating the snapshot (manual, daily or safety) was refused because the snapshot file would exceed 4 GB. |
| 500 | SNAPSHOT_IMPORT_FAILED | The restore failed while loading data; the database is left running and empty, and the snapshot stays available to retry. |
| 429 | SNAPSHOT_RATE_LIMITED | Manual snapshot creation hit its per-resource budget (1 every 5 minutes), the per-resource daily budget (5 a day) or the account’s daily budget, which depends on the plan. details.scope is resource (with retry_after), resource_daily or account (both with limit, retry_after and resets_at, the moment the day’s budget resets, in ISO 8601). |
| 429 | RESTORE_COOLDOWN | A restore or rollback was requested too soon after a previous one on the same resource. |
Custom Docker images
| Status | Code | Meaning & fix |
|---|---|---|
| 403 | PLAN_DOES_NOT_SUPPORT_CUSTOM_IMAGES | The plan does not include custom Docker images. They are available on the Pro, Business and Enterprise plans. |
| 403 | MAX_IMAGES_REACHED | The plan’s image limit was reached (2 on Pro, 6 on Business, 20 on Enterprise); images in any state count. Delete one. |
| 404 | IMAGE_NOT_FOUND | The image does not exist or you cannot access it. |
| 404 | IMAGE_VERSION_NOT_FOUND | The image has no version with that ID. |
| 400 | DOCKERFILE_NOT_FOUND | There is no file named exactly Dockerfile at the root of the build context. |
| 400 | DOCKERFILE_REFUSED | The Dockerfile has a refused instruction (# syntax= other than the official frontend, RUN --security=insecure, RUN --network=host, --mount=type=ssh or --mount=type=secret). details lists the findings; see Check Dockerfile. |
| 413 | IMAGE_CONTEXT_TOO_LARGE | The build context is over one of its four limits: 100 MB compressed, 512 MB uncompressed, 50,000 entries, or a Dockerfile larger than 1 MB. Remove files (for example with a .dockerignore) and send it again. |
| 409 | IMAGE_BUILD_IN_PROGRESS | The image already has a build running. Wait for it to end. Also returned when you try to delete the image during a build. |
| 409 | IMAGE_NOT_READY | The image has no successful build yet, so apps cannot be created from it. |
| 400 | IMAGE_NAME_MISMATCH | confirm_name is not the image name. |
| 403 / 409 | IMAGE_TEMPLATE_ONLY | The image belongs to a template and can only be used through it. Creating an application from it answers 403; rebuilding, committing to, linking a repository to or editing the context of it answers 409. |
| 409 | IMAGE_IN_USE | The image is the source of the current version of one of your private templates. details.templates lists them. Remove or replace it in the template first. |
| 503 | IMAGE_BUILDER_UNAVAILABLE | No host can build images right now. Try again later. |
| 400 | IMAGE_APP_FIELD_NOT_APPLICABLE | You sent main_file, language, version, start_command or build_command for an application that runs a custom image. These come from the image; leave them out. |
| 409 | IMAGE_APP_CODE_NOT_APPLICABLE | The application runs a custom image, so code cannot be written into /app or deployed from GitHub on it. Update the image instead. |
| 400 | IMAGE_APP_PORT_REQUIRED | The application runs a custom image and has no port yet (it was created without web). Send port when publishing it. See Publish Application. |
| — | IMAGE_LABEL_REFUSED | Build failure: the image sets a label in a namespace reserved for the platform (such as vertra.*; the build log names the refused label). Remove the label and rebuild. |
| — | IMAGE_VOLUME_REFUSED | Build failure: the image declares a VOLUME at /app or below. /app is already the application’s persistent disk. Remove the instruction and rebuild. |
| — | IMAGE_TOO_LARGE | Build failure: the built image is larger than 10 GB uncompressed. It appears in the image’s error and build log. Remove files or use a smaller base image. |
Templates
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | TEMPLATE_NOT_FOUND | The template does not exist, was removed, or is not visible to you. |
| 404 | TEMPLATE_VERSION_NOT_FOUND | The template has no version with that number or ID. |
| 409 | TEMPLATE_SLUG_TAKEN | Another template of yours already uses the slug. Choose another. |
| 400 | TEMPLATE_MANIFEST_INVALID | The manifest is not valid. details.issues lists each problem. |
| 400 | TEMPLATE_INPUT_INVALID | An answer to a question is missing or invalid. details.issues lists the questions. |
| 400 | TEMPLATE_SOURCE_REQUIRED | A service that runs code was sent without its source file. |
| 400 | TEMPLATE_SECRET_IN_SOURCE | The code sent to become a template holds a secret (a .env file, a private key or a token). details.files lists the paths. Remove the secret and send it again. |
| 404 | TEMPLATE_GITHUB_MANIFEST_NOT_FOUND | The linked repository has no template manifest at its root. |
| 403 | TEMPLATE_PLAN_REQUIRED | Your plan is below what the template needs. details names the reason and the plan required. |
| 400 | TEMPLATE_RAM_INSUFFICIENT | Not enough free memory for the template. details carries the memory needed and the memory free. |
| 403 | TEMPLATE_IMAGE_SLOTS_EXHAUSTED | The template runs an image and every custom image slot of the account is in use. Delete an image you no longer use. |
| 403 | TEMPLATE_CLOSED | The route reads or changes the code, files, commands or variables of an application that comes from a closed template. |
| 403 | TEMPLATE_SETTING_LOCKED | The deploy setting is locked by the template of the application (details.setting: zero_downtime or auto_rollback). |
| 403 | TEMPLATE_PRIVATE_LIMIT_REACHED | The limit of 5 private templates per account was reached. Delete one first. |
| 403 | TEMPLATE_SAVE_NOT_ALLOWED | An application that came from a share link cannot be saved as a template or shared. |
| 503 | TEMPLATE_STORAGE_UNAVAILABLE | The template catalog cannot be updated right now. Try again later. |
| 409 | TEMPLATE_DEPRECATED | The template is deprecated and cannot be used for new applications. |
| 400 | TEMPLATE_VERSION_NOT_SELECTABLE | The template does not allow picking earlier versions. |
| 403 | MANAGED_ENV_READONLY | The variable is injected by the template and cannot be read or changed. |
| 409 | TEMPLATE_DEPLOY_IN_PROGRESS | Another template deploy or update of the account is running. Wait for it to end. |
| — | TEMPLATE_DEPLOY_FAILED | The deploy failed and everything it created was undone. details.step is the step that failed. |
| 404 | TEMPLATE_DEPLOY_NOT_FOUND | The deploy job does not exist or belongs to another account. |
| 409 | IDEMPOTENCY_KEY_CONFLICT | The Idempotency-Key was already used with a different request. Use a new key. |
| 409 | TEMPLATE_UPDATE_BLOCKED | The update cannot go ahead. details gives the reason (for example, the application is still building or there is no newer version). |
| 409 | TEMPLATE_DETACH_CONFIRMATION_REQUIRED | The change detaches the application from its template, so it stops receiving versions. Repeat the request with confirm_detach set to confirm. details.action names the change. |
| 409 | TEMPLATE_ROLLBACK_REQUIRES_DATA | The template marks the service as migrating its data on update, so rolling back only the code is blocked. Restore the snapshot the update took instead. |
| 404 | SHARE_LINK_NOT_FOUND | The share link does not exist. |
| 410 | SHARE_LINK_REVOKED | The author revoked the share link. |
| 410 | SHARE_LINK_EXPIRED | The share link expired. |
| 410 | SHARE_LINK_EXHAUSTED | The share link reached its limit of uses. |
Stacks
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | STACK_NOT_FOUND | The stack does not exist or belongs to another account. |
| 409 | STACK_MEMBER_CONFLICT | The resource is already in another stack, or is not in the same account or workspace as the stack. |
| 404 | STACK_MEMBER_NOT_FOUND | The resource is not a member of the stack. |
| 404 | STACK_BINDING_NOT_FOUND | The stack has no connection with that ID. |
| 400 | STACK_NETWORK_MODE_UNAVAILABLE | The network mode is not public. |
| 403 | STACK_SERVICE_LOCKED | The member comes from a closed template and cannot leave the stack. |
| 400 | STACK_NAME_MISMATCH | confirm_name is not the stack name. |
| 400 | STACK_TRANSFORM_EMPTY | The folder has no application, or no database, to put in a stack. |
| 409 | STACK_BLOB_PREFIX_TAKEN | The Blob folder is already used by a stack, or has files. Choose another prefix. |
restart_required and apps_to_restart: see Create Stack.
Workspaces
| Status | Code | Meaning & fix |
|---|---|---|
| 404 | WORKSPACE_NOT_FOUND | No workspace with that ID, or you’re not a member. |
| 400 | WORKSPACE_NAME_ALREADY_EXISTS | You already have a workspace with that name. |
| 400 | WORKSPACE_MEMBER_LIMIT_REACHED | The workspace’s plan-based member cap was reached; details.limit. |
| 400 | WORKSPACE_ROLE_LIMIT_REACHED | The workspace’s plan-based role cap was reached; details.limit. |
| 404 | WORKSPACE_ROLE_NOT_FOUND | No role with that ID in this workspace. |
| 400 | WORKSPACE_ROLE_NAME_ALREADY_EXISTS | Another role in the workspace already has that name (case-insensitive). |
| 400 | WORKSPACE_ROLE_IN_USE | Can’t delete a role that still has members or a pending invite assigned to it. |
| 403 | WORKSPACE_PERMISSION_DENIED | You’re not a member, your membership expired, or your role lacks the required permission. |
| 403 | WORKSPACE_PERMISSION_ESCALATION | You tried to grant/edit a permission you don’t hold yourself, or edit your own role/membership. |
| 403 | WORKSPACE_OWNER_ONLY | Only the workspace owner can perform this action. |
| 404 | WORKSPACE_INVITE_NOT_FOUND | The invite token is invalid, was revoked, accepted already, or exhausted its uses. |
| 410 | WORKSPACE_INVITE_EXPIRED | The invite existed but its validity window passed. |
| 403 | WORKSPACE_INVITE_EMAIL_MISMATCH | The logged-in account’s email doesn’t match the email this invite was sent to. |
| 400 | APP_ALREADY_IN_WORKSPACE / DATABASE_ALREADY_IN_WORKSPACE | The resource is already linked to a workspace. |
| 404 | APP_NOT_FOUND_IN_WORKSPACE / DATABASE_NOT_FOUND_IN_WORKSPACE | The resource isn’t linked to this workspace. |
| 403 | USER_NOT_IN_WORKSPACE | The target user isn’t a member of the workspace. |
| 404 | WORKSPACE_ACTION_REQUEST_NOT_FOUND | No pending action request with that ID. |
| 400 | WORKSPACE_ACTION_NOT_NEEDED | You already hold the permission for this action — perform it directly instead of requesting it. |
| 400 | WORKSPACE_ACTION_INVALID_PARAMS | The action request’s params don’t match what that action type expects. |
| 409 | WORKSPACE_ACTION_REQUEST_PENDING | A pending request for the same action and resource already exists. |
| 410 | WORKSPACE_ACTION_REQUEST_EXPIRED | The action request expired before anyone decided on it. Open a new one. |
| 409 | WORKSPACE_ACTION_REQUEST_ALREADY_DECIDED | The action request was already approved or rejected. |
| 400 | WORKSPACE_INVITES_DISABLED | The invited account turned off workspace invites by email in its settings. Ask them to turn invites back on, or share an invite link instead. |
| 409 | WORKSPACE_ALREADY_MEMBER | You are already a member of this workspace. |
| 404 | WORKSPACE_RESOURCE_FOLDER_NOT_FOUND | No resource folder with that ID. |
| 409 | WORKSPACE_RESOURCE_FOLDER_NAME_CONFLICT | Another folder at the same level already has that name. |
| 404 | WORKSPACE_RESOURCE_NOT_FOUND | The resource referenced in a folder/favorite operation doesn’t exist or isn’t yours. |
Rate limits & quotas
| Status | Code | Meaning & fix |
|---|---|---|
| 429 | RATE_LIMIT_EXCEEDED | A per-route or per-minute budget was hit. details (when per-account) carries scope, limit, used, retry_after. |
| 429 | DAILY_QUOTA_EXCEEDED | The account’s daily API request quota for its plan was reached; resets at midnight America/Fortaleza. |
| 429 | DEPLOY_RATE_LIMITED | The account’s hourly deploy budget or its own hourly rollback budget was reached. |
| 429 | SNAPSHOT_RATE_LIMITED | Manual snapshot creation budget was reached. |
| 429 | SUBDOMAIN_CHANGE_RATE_LIMITED | Subdomain change budget was reached. |
| 429 | FORCED_SNAPSHOT_RATE_LIMIT | Forced pre-operation snapshot budget was reached. |
| 429 | RESTORE_COOLDOWN | Restore or rollback cooldown hasn’t elapsed. |
General
| Status | Code | Meaning & fix |
|---|---|---|
| 400 | VALIDATION_ERROR | A parameter, query or body field failed validation; details.path names the field. |
| 400 | INVALID_PARAMS / INVALID_PAYLOAD / INVALID_RANGE | The request shape or a specific field is invalid for this route. |
| 403 | FORBIDDEN | The account isn’t allowed to perform this action on this resource. |
| 400 | MISSING_HEADERS | A header the route requires wasn’t sent. |
| 400 | INVALID_JSON | The body isn’t valid JSON (a missing quote or comma, a trailing comma, single quotes). Validate the JSON and send it with content-type: application/json. |
| 400 | EMPTY_BODY | The request has content-type: application/json but no body. Send a JSON body ({} if the route has no required fields), or drop the content-type header on requests without a body. |
| 415 | UNSUPPORTED_MEDIA_TYPE | The content-type isn’t one the route accepts. Send JSON with content-type: application/json; upload routes take multipart/form-data. |
| 400 | BAD_REQUEST | The request itself is malformed in a way no other code covers, for example a Content-Length that doesn’t match the body. Fix the request and retry. |
| 408 | REQUEST_TIMEOUT | The request body didn’t arrive in time. Retry, and check the connection if it keeps happening. |
| 500 | INTERNAL_SERVER_ERROR | Something failed on the platform side. Retry; if it persists, contact support with the request time. |
This catalog lists every code from the public API contract. A route can still answer other
standard HTTP errors (like a generic
404 for an unknown path) that don’t carry one of these
code values.