Account & support

Troubleshooting

9 min read

Build went red? This page covers the most common failures and what to do about each. Every failed build records a machine-readable error_code on the dashboard build detail panel — start there, then find the matching section below.

Build error reference

error_codeWhat it meansRefunds the credit?
BUILD_FAILEDGradle / Capacitor / native toolchain rejected your code.No — your code didn't compile.
NO_APKThe build script claimed success but no APK landed in the expected output path.No.
ICON_DOWNLOAD_ERRORWe couldn't fetch the project icon URL you provided.No — we did the work.
SYSTEM_ERRORInternal worker / API error.Yes — platform fault.
WORKER_TIMEOUTBuild ran past your tier's timeout (10 min Hobby, 30 min Starter).Yes.
WORKSPACE_ERRORWorker couldn't set up the build environment.Yes.
DOWNLOAD_ERRORCouldn't fetch your source archive from object storage.Yes.
KEYSTORE_DOWNLOAD_ERRORCouldn't fetch the signing keystore.Yes.
KEYSTORE_DECRYPT_ERRORDecrypting the per-org keystore envelope failed.No (see Keystore decrypt error for why).
EXEC_ERRORA subprocess (Gradle, apksigner, etc.) returned non-zero from a system-side step.Yes.
UPLOAD_ERRORCouldn't upload the finished APK back to object storage.Yes.

Platform faults (the "Yes" rows above) are tracked in billing.IsSystemFault and refund automatically. You should never need to email us for a credit refund on those — the dashboard credit balance reflects the refund within seconds of the build landing.

BUILD_FAILED — your code didn't compile

This is the single most common failure. By far. The build worker ran your code through npm install and then through the native build (Gradle for Android), and one of those steps returned non-zero.

Next step: open the build detail panel in the dashboard and read the log tail. We surface the last ~200 lines of stderr from the failing step, which is usually enough to spot the culprit.

Common patterns:

  • Cannot find module 'X' — a dependency is in your code but missing from package.json. Add it, re-bundle, re-upload.
  • Module not found: Error: Can't resolve 'fs' (or path, crypto) — Node-only modules referenced in a Capacitor/Vite bundle. Move that code to the native side or guard it.
  • SDK location not found / ANDROID_HOME — almost always means the source bundle ships its own gradle.properties overriding what our worker expects. Remove the line and re-upload.
  • Execution failed for task ':app:processDebugResources' — typically a missing icon or splash asset. Cross-reference with AI Checks which flags this before you build.
  • Gradle version conflict (Plugin requires Gradle X.Y) — Capacitor or RN updated their Gradle plugin past what your gradle-wrapper.properties pins. Bump the wrapper.

If the log tail isn't enough, the AI Checks panel from the same release usually has more context. The Compatibility scan in particular catches most BUILD_FAILED causes before they reach the build worker.

NO_APK — build claimed success but no APK landed

The Gradle process exited 0 but our worker couldn't find the expected APK at app/build/outputs/apk/release/app-release.apk (or the equivalent for your framework).

Almost always one of:

  1. Custom output path — your build.gradle writes to a non-default location. Either restore the default or tell us where to look in Settings > Project.
  2. AAB instead of APK — you configured a bundleRelease instead of assembleRelease. Lunadeck v1.0 ships APKs only.
  3. Multi-module project with no app module — we look for the app Gradle module. Rename or alias your main Android module to app.

This error is charged because we did spin up the worker for the whole build — but if the cause turns out to be on us, email support@lunadeck.io and we'll refund manually.

ICON_DOWNLOAD_ERROR — icon URL unreachable

You supplied a project icon URL that returned a non-2xx (or didn't respond) when our worker fetched it. Quick fixes:

  • Re-host the icon somewhere CDN-stable (your project's main asset host, S3 public, Imgur direct link).
  • Check the URL works from curl outside your local network.
  • Make sure the URL points at the image file, not a page wrapping it.

You can also clear the icon URL field entirely and we'll fall back to the default Lunadeck launcher icon for that build.

WORKER_TIMEOUT — build exceeded the tier limit

Build timeouts are tier-pinned:

  • Hobby: 10 minutes
  • Starter: 30 minutes
  • Team / Business: 60 / 120 minutes (when those tiers go self-serve)

When your project genuinely needs more, three options:

  1. Upgrade — Starter triples the budget.
  2. Cache aggressively — see Cloud Build Engine > Caching for which paths persist between builds. Cold builds are slow; warm builds are usually 2–3× faster.
  3. Slim the bundle — large node_modules directories with unused frameworks (jQuery + React + Vue all installed, etc.) are a common timeout cause. The Performance check in AI Checks flags this.

WORKSPACE_ERROR / SYSTEM_ERROR — our infrastructure failed

These two error codes mean the failure happened outside your code — usually the worker container failed to provision, ran out of disk, or hit an internal RPC. Both refund automatically.

Rerun the build. If it fails twice in a row with the same code, check the status page — there may be an ongoing incident. If status is green and you're still hitting it, email support@lunadeck.io with the build ID so we can dig into the worker logs.

DOWNLOAD_ERROR — source bundle couldn't be fetched

Three causes, in order of likelihood:

  1. Source archive expired. Retention is 7 days for Hobby, 30 days for Starter. If you wait longer than your tier's retention between upload and build trigger, the retention sweeper purges the archive and the build fails on download. Re-upload the source and rebuild.
  2. MinIO availability issue. Rare. Refunds and retrying usually fixes it.
  3. Source upload was interrupted. If your PUT to the upload URL didn't actually complete (network drop, browser tab closed), the size on disk won't match the declared size and download will fail. Re-upload from a stable connection.

This code refunds automatically.

KEYSTORE_DOWNLOAD_ERROR

Worker couldn't pull either the shared Lunadeck-debug keystore (Hobby) or your uploaded keystore (Starter+) from object storage. Rerun the build; this is essentially the same class of failure as DOWNLOAD_ERROR and refunds the same way.

KEYSTORE_DECRYPT_ERROR

We downloaded the encrypted keystore envelope but couldn't open it. Two causes:

  1. Master-key mismatch. Our worker's KEYSTORE_MASTER_KEY doesn't match what encrypted your envelope. This shouldn't happen in normal operation — if you see this, email support@lunadeck.io immediately with the build ID. We will refund this case manually (it's our infrastructure problem).
  2. Tampered envelope. The on-disk envelope was modified after upload. Almost impossible in normal operation.

Note: this code does not auto-refund (see billing.IsSystemFault — the function excludes KEYSTORE_DECRYPT_ERROR from the platform-fault set out of caution against false-positive refunds). We refund manually on request.

EXEC_ERROR — a system-side subprocess returned non-zero

Different from BUILD_FAILED — this fires when an infrastructure step (apksigner invocation, watermark injection, file copy) failed, not when your build script failed. Refunded automatically. Rerun; if it persists, email support.

UPLOAD_ERROR — couldn't upload the finished APK

Your APK actually built, but the worker couldn't write it back to object storage. Refunded automatically. Rerunning usually resolves it; if status is green and it keeps happening, the build itself is producing a file we can't store (corrupted, way oversize) — email support with the build ID.

402 INSUFFICIENT_CREDITS — out of build credits

This is an API response, not a build error code — you'll see it when you click Build rather than after a build runs. See Account & Billing > When you run out for the upgrade or wait path.

Other situations

My build has been queued for ages and never starts

The build queue normally drains within seconds. If a build sits in queued for more than ~2 minutes:

  • Check the status pagebuild_queue depth is reported there.
  • Hobby has 1 concurrent build per release. If you triggered two in quick succession, the second waits for the first to finish.

If the status page is green and your queue is short, the build will start. If it's been more than 10 minutes, email support@lunadeck.io with the build ID.

My AI Checks won't finish

AI Checks have their own queue separate from builds. They typically complete in 2–3 minutes for the full 6-check fan-out. If a check stays in running for longer, it'll eventually time out and surface as a "check failed" with a retryable banner — click Rerun on the specific check. AI Checks are advisory and never block your build, so you can always proceed without waiting.

My APK built but won't install on my device

Two common causes:

  1. Signature mismatch with an older install. Android refuses to upgrade an installed APK to one signed with a different certificate. Uninstall the previous version first. The most common version of this: you started on Hobby (Lunadeck-managed debug cert) and upgraded to Starter (your BYO cert) — Android sees the new APK as untrusted relative to the installed one. Uninstall, then install the new one.
  2. **adb install complains "INSTALL_FAILED_VERSION_DOWNGRADE"** — you're installing an older versionCodethan what's already installed. Bump theversionCode` in your build configuration and rebuild.

For Play Store-bound APKs specifically, see Certificate Vault > Bring your own keystore — Hobby builds use a shared debug certificate and cannot be uploaded to the Play Store.

Magic preview QR code won't scan

Magic preview deep-links are generated only for succeeded Android builds with a signed preview_url. If you see a placeholder card instead of a QR code on a build that succeeded, the most likely cause is the build is iOS-only (iOS doesn't support Magic preview at v1.0) or your environment has MAGIC_PREVIEW_JWT_SECRET unset — check the build's platform column.

The preview link expires when the build's expires_at is reached (7d Hobby, 30d Starter). Scan it before then.

Still stuck?

Email support@lunadeck.io with the build ID (a UUID printed at the top of the build detail panel) and a one-line description of what you were trying to do. We can pull the full worker log from your build ID.