# Error codes

| Code | Meaning | Fix |
|---|---|---|
| [DETECT_NO_PROJECT](/docs/errors/DETECT_NO_PROJECT.md) | No deployable project found in this directory. | Run deploy from the directory that contains the app's own manifest: package.json, pyproject.toml, requirements.txt, Pipfile, Gemfile, pom.xml, build.gradle, Cargo.toml or composer.json (or index.html for a site of plain files). |
| [DETECT_UNSUPPORTED_FRAMEWORK](/docs/errors/DETECT_UNSUPPORTED_FRAMEWORK.md) | The framework in this project is not supported yet. | Supported today: Next.js, Express, Hono, Fastify, any Node app with a start script that listens on process.env.PORT, Django, Flask, FastAPI, Vite, Spring Boot and Rust. Convert the app to one of those and deploy again. |
| [DETECT_PORT_FIXED](/docs/errors/DETECT_PORT_FIXED.md) | The start command forces a fixed port, so the app would never answer on the port the platform gives it. | Remove the fixed port from the "start" script in package.json. For Next.js, `next start` on its own reads PORT; for other servers, listen on process.env.PORT. Keep a fixed port in the "dev" script if you like. Then re-run deploy. |
| [ENGINE_CONVERSION_REQUIRED](/docs/errors/ENGINE_CONVERSION_REQUIRED.md) | This app uses a database engine the platform does not host. | Postgres is the platform database and every app gets one. Convert the data layer to Postgres (keep the ORM, change the provider/dialect and any engine-specific SQL), then re-run deploy. This includes SQLite: an app's disk does not survive between requests here, so a SQLite file loses what people write. |
| [AUTH_PROVIDER_AVAILABLE](/docs/errors/AUTH_PROVIDER_AVAILABLE.md) | This app has its own sign-in, but the platform already signs people in to a private app and tells the app who they are. | In a private app, remove the auth library and read the person from the x-uarpie-user-id, x-uarpie-user-email, x-uarpie-user-name and x-uarpie-user-role headers (or GET /_platform/me from browser code); the platform removes those headers from anything a visitor sends. A public app whose visitors are strangers keeps its own sign-in. This is a warning; deploy continues. |
| [SCHEDULED_WORK_IN_APP](/docs/errors/SCHEDULED_WORK_IN_APP.md) | This app runs work on a timer inside itself, and a copy of the app is paused between visits, so that work runs late, all at once, or not at all. | Move each piece of timed work to a job in deploy.json (jobs: a name, a schedule and a path the platform calls on time), and turn the timer off: Spring Session JDBC with spring.session.jdbc.cleanup-cron=-, Spring's @Scheduled by removing @EnableScheduling, Quartz with spring.quartz.auto-startup=false, APScheduler, node-cron and the like by not starting the scheduler. This is a warning; deploy continues. |
| [ASTRO_FORMS_REFUSED](/docs/errors/ASTRO_FORMS_REFUSED.md) | This Astro app runs as a server, and its config does not let it read the address visitors open, so here it would refuse every form its own pages send ("Cross-site POST form submissions are forbidden") and Astro.url would say http://localhost. | Add security: { allowedDomains: [{ protocol: "https" }] } inside defineConfig({ ... }) in astro.config.mjs, then deploy again. Astro then reads the visitor's address from X-Forwarded-Host, which the platform sets on every request and visitors cannot, custom domains included, and still refuses forms sent from other sites. This is a warning; deploy continues. |
| [PARTS_MIGRATE_AFTER_MAIN](/docs/errors/PARTS_MIGRATE_AFTER_MAIN.md) | The main part goes live and is checked before another part runs its migrations, so on the first deploy its home page meets a database with no tables yet. | Nothing to do: if the main part's first check fails, the deploy carries on with the other parts and deploys the main part again after them. To save that second build, make its home page answer while the tables do not exist yet (a short "being set up" page rather than an error). This is a warning; deploy continues. |
| [BUILD_FAILED](/docs/errors/BUILD_FAILED.md) | The build failed. | Fix the first error in details.excerpt, then re-run deploy. If there is no excerpt, run the project's own build command on this machine (for example npm run build), fix the first error it prints, then re-run deploy. |
| [BUILD_MISSING_DEPENDENCY](/docs/errors/BUILD_MISSING_DEPENDENCY.md) | Build failed: a module the code imports is not in package.json. | Add the missing module to dependencies in package.json, then re-run deploy. |
| [BUILD_TIMEOUT](/docs/errors/BUILD_TIMEOUT.md) | The build ran longer than the limit and was stopped. | Remove heavy postinstall steps or large assets from the build, then re-run deploy. |
| [BUILD_IMAGE_TOO_LARGE](/docs/errors/BUILD_IMAGE_TOO_LARGE.md) | The built app is larger than the runtime limit. | Move large files out of the app into its file storage (upload with POST /_platform/upload-url, read with GET /_platform/file-url) and remove unused dependencies, then re-run deploy. |
| [BUILD_SETTING_MISSING](/docs/errors/BUILD_SETTING_MISSING.md) | A setting the app reads while it is being built is not set, so the built app has it empty. | Set it with `npx uarpie -e NAME=value`, using the setting's name and its real value. That deploys again and rebuilds the app with the value in it. Changing it later also needs a rebuild; the platform does that by itself when the setting is changed in the dashboard. |
| [MIGRATE_FAILED](/docs/errors/MIGRATE_FAILED.md) | The database migration failed. | Fix what the migration reported, then re-run deploy. When the platform could read the migration's output, message quotes the error it printed and details.excerpt holds its last lines. The version that was live before, if any, is still serving. |
| [PROVISION_FAILED](/docs/errors/PROVISION_FAILED.md) | The platform could not create a resource this app needs. | Re-run deploy. If it fails again with the same code, nothing in the project is wrong; report the deployment_id. |
| [ROUTE_FAILED](/docs/errors/ROUTE_FAILED.md) | The app was built but could not be given an address. | Re-run deploy. The build is cached, so this is fast. |
| [ADDRESS_STILL_STARTING](/docs/errors/ADDRESS_STILL_STARTING.md) | The app is live, but its new address is still being set up at the edge and can take up to a minute more to answer. | Wait a minute before opening the link. Do not deploy again; that does not make the address ready sooner. |
| [DOMAIN_INVALID](/docs/errors/DOMAIN_INVALID.md) | That is not a domain name the platform can attach. | Use a hostname you control, like app.example.com, without a scheme or path. |
| [HEALTHCHECK_FAILED](/docs/errors/HEALTHCHECK_FAILED.md) | The new version started but did not answer a request. | Make sure the server listens on process.env.PORT and answers GET / soon after it starts. Read the app's startup output with the logs command or tool, then re-run deploy. If an earlier version was live, it still is. |
| [DEPLOY_IN_PROGRESS](/docs/errors/DEPLOY_IN_PROGRESS.md) | Another deploy of this project is already running. | Wait for the running deployment to finish (poll status with deployment_id), then re-run deploy. |
| [DEPLOY_STILL_RUNNING](/docs/errors/DEPLOY_STILL_RUNNING.md) | The app is still deploying. This command stopped watching, but the platform is still working on it. | Wait a few minutes, then run `npx uarpie status` to see whether it went live. Do not start another deploy: one is already running and a second would be refused. |
| [REQUEST_TOO_LARGE](/docs/errors/REQUEST_TOO_LARGE.md) | The request carried more than 1 MB, which is more than an app can be sent in one request. | Upload files straight to the app's storage instead of posting them to the app: ask the platform for an upload link (POST /_platform/upload-url) and send the file there. That route answers somebody the app knows — a signed-in visitor, the owner, or an api-kind app's key — so in a public app the upload has to be made by the app's own server code, using UARPIE_STORAGE_BUCKET and UARPIE_STORAGE_PREFIX. Forms and other posts must stay under 1 MB. |
| [APP_PAUSED](/docs/errors/APP_PAUSED.md) | This app is paused, so it cannot answer right now. | Nothing is lost: when it comes back, it is exactly as it was. If the app is yours, your Uarpie dashboard says why it is paused and how to bring it back. |
| [NOTHING_CHANGED](/docs/errors/NOTHING_CHANGED.md) | The live version already has these files, settings and deploy.json, built on the platform as it is now. | No action needed. Change a file, a setting or deploy.json, then run deploy again. |
| [SCHEMA_DRIFT](/docs/errors/SCHEMA_DRIFT.md) | Rolling back code only: the database schema changed after the target version. | If the older code fails against the current database, go forward again to the newer version, or fix the code forward. Restoring the data from before a change is not available yet. |
| [UNAUTHORIZED](/docs/errors/UNAUTHORIZED.md) | No valid credential was presented. | Run deploy from the same machine that created the project, or sign in with `uarpie login`. |
| [FORBIDDEN](/docs/errors/FORBIDDEN.md) | This credential cannot act on that project. | Use the account that owns the project, or ask the owner to invite you. |
| [BIND_TOKEN_EXPIRED](/docs/errors/BIND_TOKEN_EXPIRED.md) | This link has expired. | Re-run deploy to mint a fresh link. It costs nothing. |
| [BIND_TOKEN_CONSUMED](/docs/errors/BIND_TOKEN_CONSUMED.md) | This app is already in use on another device. | Ask the owner to invite you by email from the app's share screen. |
| [PROJECT_NOT_FOUND](/docs/errors/PROJECT_NOT_FOUND.md) | No project with that id exists. | Check deploy.json in the project root, or run list_projects. |
| [PROJECT_EXPIRED](/docs/errors/PROJECT_EXPIRED.md) | This app was never claimed and has been removed. | Run deploy again: it makes a new app at a new address. If it answers with this again, remove project.id from deploy.json first. Then open the link and choose "keep this app". |
| [ACCOUNT_REQUIRED](/docs/errors/ACCOUNT_REQUIRED.md) | This action needs a claimed account. | Open the app's link and choose "keep this app" to claim it, then retry. |
| [CLIENT_LINK_INVALID](/docs/errors/CLIENT_LINK_INVALID.md) | This link to claim an app has been used, has been replaced by a newer one, or has run out. | Ask the person who made the app for a new link. They make one on the app's page in their dashboard, or with `npx uarpie client` in its folder. |
| [CLIENT_EMAIL_MISMATCH](/docs/errors/CLIENT_EMAIL_MISMATCH.md) | This app was offered to a different email address than the one signed in. | Sign in with the address the link was sent to, or ask the person who made the app for a new link to this address. |
| [CLIENT_ALREADY_CLAIMED](/docs/errors/CLIENT_ALREADY_CLAIMED.md) | The client has already claimed this app, so it is theirs. | Nothing to do: it stays in your list as a client's app, and you can go on building it unless the client takes you off it. Changes to who owns it are now the client's to make. |
| [SPEND_CAP_REACHED](/docs/errors/SPEND_CAP_REACHED.md) | This would cost more than the account's spending limit allows this month, so it was not started. | Nothing was charged and nothing was started. No code change will help. Use above a plan is paid from the account's Uarpie credit, up to the spending limit the owner sets for the month: the owner can raise the limit or top up the credit in the dashboard, under Billing. Tell the person it was refused, and why. |
| [CREDIT_NEEDED](/docs/errors/CREDIT_NEEDED.md) | This is paid from Uarpie credit, and the account has none left, so it was not started. | Nothing was charged. No code change will help. The owner tops up in the dashboard, under Billing: any amount from $5 to $1,000, and credit never expires. Then ask for it again. |
| [RATE_LIMITED](/docs/errors/RATE_LIMITED.md) | Too many requests in a short time. | Wait for the number of seconds in details.retry_after, then retry. |
| [DEPLOY_LIMIT_REACHED](/docs/errors/DEPLOY_LIMIT_REACHED.md) | Today's limit for deploying without an account has been reached: this address's own, or, when Uarpie is busy, the one shared by everyone. | For an app already deployed, keep it (free: the person opens its link and chooses Keep this app), then deploy again. Before deploying a new app, run `uarpie login` (free: the person approves it in their browser). Kept apps and signed-in deploys are not limited this way. Otherwise wait for the number of seconds in details.retry_after. |
| [MAIL_UNDELIVERABLE](/docs/errors/MAIL_UNDELIVERABLE.md) | The email could not be sent. | Check the address for a typo, then try again. Nothing about the app itself is affected — this is only the email. |
| [EGRESS_NOT_ALLOWED](/docs/errors/EGRESS_NOT_ALLOWED.md) | The app tried to reach a service it is not allowed to reach. | An app nobody has kept yet can only reach a short list of common services. Ask the person to keep the app — the link was printed when it was deployed — and it will be able to call any service. The refusal names the host it was trying to reach. |
| [EGRESS_BLOCKED](/docs/errors/EGRESS_BLOCKED.md) | This app has been stopped from calling outside services. | The app itself is still running and still serving visitors; only its calls to the outside are refused. Write to support@uarpie.app if you think this is wrong. |
| [EGRESS_LIMIT_REACHED](/docs/errors/EGRESS_LIMIT_REACHED.md) | This app has reached how much it may call outside services. | Wait and try again. If the app opens a new connection for every request, reusing connections will fix it; most HTTP clients do that with a keep-alive agent. |
| [EGRESS_NEEDS_KEEP](/docs/errors/EGRESS_NEEDS_KEEP.md) | The app's home page calls a service that an app nobody has kept yet cannot reach. | Ask the person to keep the app: open its address and sign in. A kept app can reach any service. Then deploy again. Nothing in the code needs to change. |
| [DESKTOP_SHELL_UNAVAILABLE](/docs/errors/DESKTOP_SHELL_UNAVAILABLE.md) | Desktop apps cannot be made for that system right now. | Nothing in the app is wrong. The platform's desktop shell for that operating system has not been published; try again later or choose another system. |
| [PLAN_LIMIT_REACHED](/docs/errors/PLAN_LIMIT_REACHED.md) | The account's plan does not allow this. | The owner can upgrade the plan in the dashboard, or remove an existing project. |
| [USAGE_LIMIT_REACHED](/docs/errors/USAGE_LIMIT_REACHED.md) | This account has used the build minutes its plan includes this month, so new builds wait. | Nothing was built and nothing was charged. Running out of build minutes does not stop apps that are already live. Deploys work again from the first day of next month, or sooner if the owner adds Uarpie credit or moves to a bigger plan in the dashboard. No code change will help. |
| [SECRETS_TOO_LARGE](/docs/errors/SECRETS_TOO_LARGE.md) | The app's settings exceed the 4 KB runtime limit. | Move large values (certificates, JSON blobs) to a file in storage and reference it by key. |
| [STORAGE_ALMOST_FULL](/docs/errors/STORAGE_ALMOST_FULL.md) | The file space in this app's plan is almost used up. All the apps on one account share it. | Nothing is refused yet. Delete files the apps no longer need, or move to a plan with more room before it fills up. Personal includes 10 GB for all of an account's apps together, Family 50 GB and Community 100 GB; an app nobody has kept yet has 1 GB of its own. Tell the person: only they can change the plan. |
| [STORAGE_FULL](/docs/errors/STORAGE_FULL.md) | The file space in this app's plan is used up, so no upload link was given for that file. All the apps on one account share it. | Delete files the apps no longer need and ask for the link again, or tell the person to move to a plan with more room. Personal includes 10 GB for all of an account's apps together, Family 50 GB and Community 100 GB; an app nobody has kept yet has 1 GB of its own. The app itself keeps running and nothing is deleted. Note that this refuses upload links only: a file the app writes for itself through UARPIE_STORAGE_BUCKET still goes through, and still counts. |
| [FILE_LINK_EXPIRED](/docs/errors/FILE_LINK_EXPIRED.md) | This link to one of the app's files has expired. | A file link works for at least half an hour. Ask for the file again (GET /_platform/file-url gives a new link), or reload the page that showed it. |
| [FILE_LINK_INVALID](/docs/errors/FILE_LINK_INVALID.md) | This link to one of the app's files is not one the platform made, or it was changed after it was made. | Use the link exactly as GET /_platform/file-url gave it, with its e= and t= parts. A link that was cut short or edited will not work: ask for the file again. |
| [SOURCE_UNAVAILABLE](/docs/errors/SOURCE_UNAVAILABLE.md) | The source for that version is no longer stored. | The source of a version is kept for 30 days, and always for the newest five versions. Clone the live version instead: leave out --at (or `version` in the clone tool). |
| [VERSION_UNAVAILABLE](/docs/errors/VERSION_UNAVAILABLE.md) | That version can no longer be brought back: each app keeps its last 10 builds ready. | Choose one of the last 10 versions. To run older code again, deploy it from the app's folder, or clone that version first while its source is kept (the newest five versions, and any from the last 30 days). |
| [LOGIN_CODE_INVALID](/docs/errors/LOGIN_CODE_INVALID.md) | That sign-in code is not valid or has expired. | Run `uarpie login` again to get a fresh code and open the link it prints within 10 minutes. |
| [MOBILE_STORE_STEP_REQUIRED](/docs/errors/MOBILE_STORE_STEP_REQUIRED.md) | This is a phone app: every deploy makes the version phones run, and its address shows a page saying so. For now it opens on a phone in Expo Go, Expo's free app, and the platform does not publish to the App Store or Google Play. | Tell the person their phone app is deployed. To open it on their phone, they install Expo Go (free, from the App Store or Google Play), then on the app's page in their Uarpie dashboard choose "Open it on your phone" and scan the code with the phone's camera; people they invite open it from the app's link there. Expo Go runs only apps made with the Expo SDK it is for (57 today) and only Expo's built-in features. Do not send anyone to its address to use it: that only says it is a phone app. A store listing is a separate step that needs their own Apple Developer account ($99/year) or Google Play account ($25) and a review by Apple or Google. Do not promise a store release or a date. |
| [MOBILE_NO_SERVER_FOUND](/docs/errors/MOBILE_NO_SERVER_FOUND.md) | This is a phone app with no server in the folder, so there is nothing to host yet. | If the app talks to a backend, deploy that folder instead. If it needs one, create it (an Express or Hono server with the API routes the app calls) and deploy that; then point the app at the URL the deploy prints. |
| [PHONE_VERSION_MISSING](/docs/errors/PHONE_VERSION_MISSING.md) | This app has no phone version yet. | A phone version is made on every deploy of a phone app (an Expo project). Deploy the app again; if the deploy says it could not make the phone version, fix the first error it names and deploy once more. |
| [PHONE_LINK_INVALID](/docs/errors/PHONE_LINK_INVALID.md) | This link to the app's phone version has expired or was taken back. | Ask for a fresh link to the app's phone version and open that one instead. |
| [VIEWER_CODE_UNKNOWN](/docs/errors/VIEWER_CODE_UNKNOWN.md) | No app has this code. | Check the six letters and numbers, or scan the code again. A code looks like HK7QM2. |
| [VIEWER_SIGN_IN_REQUIRED](/docs/errors/VIEWER_SIGN_IN_REQUIRED.md) | This app is private. Sign in with the email it was shared with to open it. | Sign in with the email address the owner invited, then open the app again. |
| [VIEWER_NOT_INVITED](/docs/errors/VIEWER_NOT_INVITED.md) | This email has not been invited to this app. | Ask the app's owner to share it with this email address, or sign in with the email it is shared with. |
| [SIGN_IN_CODE_INVALID](/docs/errors/SIGN_IN_CODE_INVALID.md) | That code is not right, or it has expired. | Type the six digits from the latest email we sent. If it is more than ten minutes old, ask for a new code. |
| [PUBLISH_NOT_CONFIRMED](/docs/errors/PUBLISH_NOT_CONFIRMED.md) | Making an app public is not something to do on someone's behalf without asking. | Tell the owner in plain words that anyone with the address will be able to open the app and see what is in it, and that invitations stop being needed. Only if they agree, call again with confirm_public: true. If they are unsure, leave it private; it can be published later at any time. |
| [STORE_ACCOUNT_REQUIRED](/docs/errors/STORE_ACCOUNT_REQUIRED.md) | Uploading to the App Store or Google Play for the person is not available. | Ask for the build to be handed back instead (the build_app tool, or mode handback): the person gets the files and uploads them to the store themselves, with their own developer account. Do not promise a store upload or a date. |
| [STORE_CREDENTIAL_REJECTED](/docs/errors/STORE_CREDENTIAL_REJECTED.md) | Apple or Google refused the connected developer credential. | The key was probably revoked, expired, or lacks permission. Ask the owner to generate a new one in their developer console and connect it again. Do not retry with the same credential. |
| [RELEASE_IN_PROGRESS](/docs/errors/RELEASE_IN_PROGRESS.md) | A phone build for that platform is already under way for this app. | Wait for it to finish (check with the releases tool), then start another only if it is still needed. |
| [PHONE_BUILD_LIMIT_REACHED](/docs/errors/PHONE_BUILD_LIMIT_REACHED.md) | This account has made the 10 Android builds an account may make in a month, so this one was not started. | Nothing was built and nothing was charged. Tell the person the account can build again from the first day of next month (details.resets_at). Live apps are not affected, files from earlier builds are unaffected, and no code change will help. |
| [RELEASE_PLATFORM_UNAVAILABLE](/docs/errors/RELEASE_PLATFORM_UNAVAILABLE.md) | That phone build is not available yet. | Tell the person it is not available yet, and do not promise a date. Nothing was built and nothing was charged. |
| [RELEASE_NOT_CONFIRMED](/docs/errors/RELEASE_NOT_CONFIRMED.md) | A phone build needs a current price quote, and that exact amount confirmed, before it starts. | Call estimate_release first, show the person the exact amount it returns, and only after they agree call release again passing that quote_id and the same confirm_cents. Never guess the number. |
| [RELEASE_QUOTE_EXPIRED](/docs/errors/RELEASE_QUOTE_EXPIRED.md) | That price quote is no longer current. | Call estimate_release again for a fresh quote, show the person the new amount, and retry with it. |
| [DOMAIN_UNAVAILABLE](/docs/errors/DOMAIN_UNAVAILABLE.md) | That domain cannot be bought here. | Show the person the note in details — taken, premium, or an ending the platform does not sell — and offer the alternatives in details.alternatives, or use add_domain with a domain they already own. |
| [DOMAIN_QUOTE_EXPIRED](/docs/errors/DOMAIN_QUOTE_EXPIRED.md) | That price quote is no longer current. | Call buy_domain again without quote_id for a fresh quote, show the person the new confirmation sentence, and retry with the new quote_id once they say yes. |
| [DOMAIN_ORDER_IN_PROGRESS](/docs/errors/DOMAIN_ORDER_IN_PROGRESS.md) | A domain is already being bought for this app. | Wait for it to finish; read it with GET /v1/projects/{id}/domains/orders. One domain order at a time per app. |
| [AI_CREDITS_EMPTY](/docs/errors/AI_CREDITS_EMPTY.md) | This account has used all its Uarpie credit, so Uarpie Builder stopped before this step. | Nothing was charged for the step that was refused, and the work so far is kept. Top up on the credit page (in the builder: the ring beside the model, then Top up), then send the message again and the builder carries on from where it stopped. |
| [AI_UNAVAILABLE](/docs/errors/AI_UNAVAILABLE.md) | Drafting with AI is not set up on this server. | Set ANTHROPIC_API_KEY in the API's environment and restart it. Until then, write the page by hand; nothing else is affected. |
| [SITE_PAGE_PROTECTED](/docs/errors/SITE_PAGE_PROTECTED.md) | That page is part of the site itself and cannot be deleted or given a different address. | Edit its content instead. To take it out of the navigation, turn off nav.show. Only pages created in the console can be deleted. |
| [SITE_SLUG_TAKEN](/docs/errors/SITE_SLUG_TAKEN.md) | Another page or post already uses that address. | Choose a different slug, or edit the existing page instead of creating a second one. |
| [INTEGRATION_NOT_CONFIGURED](/docs/errors/INTEGRATION_NOT_CONFIGURED.md) | That figure comes from an outside service that is not connected. | Set the environment variables named in details.variables on the API and restart it. |
| [EDGE_NOT_UPDATED](/docs/errors/EDGE_NOT_UPDATED.md) | The change is saved, but the edge that checks who may open the app has not caught up yet. | Nothing to do: the platform tells the edge again by itself within about five minutes. To make it sooner, run the same request again; it is safe to repeat. Until the edge hears, a key you revoked may still open the app. |
| [VALIDATION_FAILED](/docs/errors/VALIDATION_FAILED.md) | The request was malformed. | Check details.issues for the fields that failed validation and correct them. |
| [INTERNAL](/docs/errors/INTERNAL.md) | Something went wrong on the platform. | Retry once. If it fails again, report the request_id in details. |
