Unity Cloud Build Android Fails After AGP or JDK Update - Gradle Pipeline Fix - How to Fix

Problem: Your Unity project built on Unity Cloud Build for Android was working, then started failing after you or a plugin updated the Android Gradle Plugin (AGP), Gradle wrapper version, compileSdk / targetSdk, or JDK expectations. Local Editor builds might still work (or might not), but Cloud Build logs show Gradle errors, Java toolchain failures, or dependency resolution explosions.

Typical log phrases people search for:

  • Minimum supported Gradle version is X or Gradle version X is required
  • Unsupported class file major version (JDK bytecode mismatch)
  • Dependency requires Android Gradle plugin X or compileSdkVersion / namespace errors
  • Could not resolve all dependencies after External Dependency Manager or Firebase / Play libraries bumped
  • Incompatible JVM target or Android Gradle plugin requires Java 17

Root cause: Android builds are a stacked toolchain. Unity pins an embedded JDK for the Editor, but Cloud Build agents use the JDK configured for the target (or Unity’s documented default for that Editor version). When AGP jumps (often via Google Play, Ads, or Firebase packages), it may require Gradle 8.x and JDK 17. If Cloud Build still resolves with an older JDK or an old gradle-wrapper.properties, Gradle fails before your game code runs. Separately, custom Gradle templates (mainTemplate.gradle, baseProjectTemplate.gradle) edited for one AGP version can break when the wrapper or buildscript blocks drift from what Unity expects on CI.

This guide gives a fix order that survives real projects with EDM4U / External Dependency Manager and mixed native SDKs.

Quick fix checklist

  1. Read the first Gradle error in Cloud Build (not the last cascaded line).
  2. Confirm JDK 17 for the Cloud Build Android target if AGP is 8.x (Unity 6.x / 2022.3+ style stacks).
  3. Align Gradle wrapper to the minimum AGP requires (see Android Gradle Plugin release notes).
  4. Re-export Gradle templates from Unity and re-apply minimal custom edits.
  5. Run Android Resolver (EDM4U) locally, commit mainTemplate.gradle / settingsTemplate.gradle only if your workflow requires it.
  6. Rebuild from a clean clone to match CI.

Step 1: Capture the real failure line in Cloud Build

  1. Open Unity Dashboard → Cloud Build → Builds → failed build.
  2. Expand Android / Gradle logs.
  3. Scroll to the first FAILURE: Build failed with an exception block.
  4. Note whether it mentions Gradle version, Java, compileSdk, namespace, or could not resolve.

Verification: You have one primary bucket (JDK vs Gradle vs SDK vs dependency). Fix that bucket first; ignore hundreds of downstream resolver errors until the toolchain matches.


Step 2: Match JDK on Cloud Build to AGP (usually Java 17)

Modern AGP (8.x) expects JDK 17.

  1. In Unity Dashboard → Cloud Build → your target → Environment (or target advanced settings, depending on dashboard revision), set the Android JDK option to JDK 17 if your Unity version exposes it.
  2. If the UI does not show JDK: use Unity version that ships Android builds with documented JDK 17 support for your base Gradle/AGP stack, or add a documented pre-build step only if your org’s Cloud Build plan supports injecting toolchain (many teams fix this purely by Unity version + templates).
  3. Locally, open Edit → Preferences → External Tools and note which JDK Unity uses. Do not assume Cloud Build matches; explicitly align per Unity’s Cloud Build docs for your Editor version.

Verification: Re-run Cloud Build. Errors like Unsupported class file major version 65 (Java 21 class files on a Java 17 runner) or major version 61 mismatches should change or disappear when JDK and compiled plugin bytecode align.

Alternative: If a third-party .jar was built with a newer Java than your cloud JDK, either recompile that dependency for bytecode compatibility or raise the cloud JDK only if Unity supports it for that pipeline.


Step 3: Align Gradle wrapper to AGP

AGP releases document minimum Gradle versions.

  1. Open Assets/Plugins/Android/gradleTemplate.properties only if you customize it; otherwise check Unity’s generated Gradle area after an local Android export for reference.
  2. Inspect gradle-wrapper.properties (often under Assets/Plugins/Android when using custom templates, or generated in Library/Bee/Android after a build). The distributionUrl must satisfy AGP’s minimum (for example Gradle 8.x for current AGP 8.x lines).
  3. In Unity Project Settings → Player → Android → Publishing Settings, enable Custom Main Gradle Template / Custom Gradle Properties Template only when needed. If you previously hand-edited templates for an older stack, disable custom templates, let Unity regenerate, then re-apply only the minimal lines you truly need (signing config is usually kept in Unity UI, not hard-coded secrets).
  4. If EDM4U injects mainTemplate.gradle dependencies, run Assets → External Dependency Manager → Android Resolver → Force Resolve locally, then commit the changed Gradle files your team tracks.

Verification: Local command-line Gradle build is optional; the decisive test is Cloud Build with the same committed templates. The error downgrading from “Gradle too old” to a specific missing dependency means this step worked.


Step 4: Fix Android SDK / compileSdk drift after library updates

After updating Google Play, Firebase, or mobile ads SDKs, libraries may require a higher compileSdk.

  1. Edit → Project Settings → Player → Android: raise Target API Level and minimum API per store requirements and library manifests.
  2. Ensure Android SDK tools on Cloud Build are sufficient: large jumps sometimes need a newer Unity patch so the managed SDK bundles align.
  3. If a dependency error names coreLibraryDesugaring, namespace, or manifest merger, open the full merger log in Cloud Build; often the first conflict lists the offending package version.

Verification: Build reaches compileDebugJavaWithJavac or compileReleaseKotlin without compileSdk errors.


Step 5: EDM4U and duplicate class / resolution failures

Symptoms: Program type already present, duplicate Google Play libraries, or “mixed versions” of play-services-*.

  1. Run Force Resolve with a single chosen BOM or version strategy (avoid half-updated *Dependencies.xml files).
  2. Remove manual implementation lines that duplicate what EDM4U already injects unless you fully own that merge path.
  3. Commit ProjectSettings/AndroidResolverDependencies.xml (or your project’s agreed resolver state) so CI resolves the same graph.

Verification: Resolver finishes cleanly locally; Cloud Build no longer stops at duplicate classes.


Step 6: When local succeeds but Cloud Build fails

  1. Confirm the same Unity version and same branch; no “local only” uncommitted Gradle edits.
  2. Delete Library locally, reopen, and build Android again.
  3. Compare Environment variables (signing, keystore paths, API keys) only if Gradle references them; missing secrets usually fail signing, not AGP sync—but custom Gradle can reference env vars.
  4. Reduce parallelism hacks in custom Gradle (workers, daemon flags) that behave differently on CI.

Prevention tips

  • Treat AGP / Gradle / JDK as a triple lock: bump one only with a note for the other two.
  • After major mobile SDK updates, schedule a throwaway Cloud Build before release week.
  • Keep one source of truth for Android dependencies (prefer EDM4U over ad-hoc implementation copies).
  • Document your Target API compliance date so library bumps are not surprise Friday failures.

FAQ

Does Unity Cloud Build use the same JDK as my Editor?
Not necessarily. Always set the Android target’s toolchain explicitly and confirm against Unity’s documentation for your Editor version.

Should I check in the entire Library folder?
No. Commit project settings, Packages/manifest.json, packages-lock.json, and whatever Gradle templates your team has agreed to version.

Is raising compileSdk enough when Play Console warns?
Often yes for compilation, but store policy also cares about targetSdk and behavior changes; raise both intentionally and test on device.

Related problems and links

Bookmark this fix if you maintain Android CI; share it when someone says “it worked until we updated the Firebase package.”