·9 min read

Gradle build failed is not the error

Unity prints CommandInvokationFailure, then 67 environment variables. The line saying what broke is 235 lines up. Four failures I reproduced in Unity 6000.4.

unityandroidgradlegamedev

Contents

I made a throwaway Unity 6000.4.0f1 project on a Mac, built a release APK from it, and then broke it four different ways to see what the console would say. The APK took 155 seconds in the Gradle stage and came out at 27 MB. Every failure below is from a log on this laptop, not from a thread I read.

The reason for doing it that way: the message everybody searches for says nothing about the project that produced it, and I wanted to know exactly where the part that does say something had gone.

The error you searched for

However you phrased it — unity gradle build failed, unity error gradle build failed, unity commandinvokationfailure gradle build failed, unity android build failed gradle, unity 6 gradle build failed, unity 2022 gradle build failed, unity firebase gradle build failed — you got here from the same eleven words in the console:

CommandInvokationFailure: Gradle build failed. 
/Applications/Unity/Hub/Editor/6000.4.0f1/PlaybackEngines/AndroidPlayer/OpenJDK/bin/java -classpath "/Applications/Unity/Hub/Editor/6000.4.0f1/PlaybackEngines/AndroidPlayer/Tools/gradle/lib/gradle-launcher-8.13.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx4096m" "assembleRelease" 

Environment Variables:
XPC_FLAGS = 0x0
CLAUDE_CODE_ENTRYPOINT = claude-vscode
ANDROID_NDK_ROOT = /Applications/Unity/.../AndroidPlayer/NDK
LANG = C.UTF-8

Sixty-seven of those variables in my log, and I counted them because I kept scrolling through them looking for the reason. There isn't one in there. Unity started a Java process, the process exited 1, and this block is Unity reporting which process it started. It would look identical if the failure were a missing semicolon or a full disk.

Which is why the answers on the first page of results are all different from each other, and why none of them worked for you. They are answers to different questions that produce the same sentence.

Where the cause actually is

Here is the shape of the whole log for one failing build of an otherwise empty project — 1,572 lines, from a project with one scene and no gameplay in it:

Line What is there
171 CommandInvokationFailure: Unable to list connected devices — unrelated, no phone plugged in
174 environment variables, first dump
921 FAILURE: Build failed with an exception.
923 * What went wrong: — the cause
926–1336 410 lines listing every duplicate class
1140 BUILD FAILED in 6s
1158 CommandInvokationFailure: Gradle build failed.
1161 environment variables, second dump, 67 of them
1231 the whole thing again, quoted inside the exception

The line that tells you what to fix comes 235 lines before the line that tells you something failed. Everyone scrolls down from the error. The answer is up.

Two other things that log shows. There is a CommandInvokationFailure at line 171 that has nothing to do with the build — it is ADB failing to list devices, with its own full environment dump, and in a headless build it is always there. And the real Gradle output is printed twice: once as it happens, once again quoted inside the exception body at line 1231, which is why the log looks twice as bad as the problem is.

So stop reading the console and grep the log:

grep -A6 '^\* What went wrong:' ~/Library/Logs/Unity/Editor.log

That worked on every failure I produced. The Editor log is at ~/Library/Logs/Unity/Editor.log on macOS, %LOCALAPPDATA%\Unity\Editor\Editor.log on Windows, ~/.config/unity3d/Editor.log on Linux.

Unity also saves the Gradle run on its own, which is cleaner to read and easier to link to a colleague:

Library/Bee/Android/Prj/<Mono2x|IL2CPP>/Gradle/launcher/build/outputs/logs/unity-assembleRelease-build.log

Four builds I broke on purpose

One jar, twice

I copied unity-classes.jar out of the generated Gradle project into Assets/Plugins/Android/ under a second name, which is structurally what happens when two plugins each vendor the same library.

* What went wrong:
Execution failed for task ':launcher:checkReleaseDuplicateClasses'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
   > Duplicate class com.unity3d.player.UnityPlayer found in modules unity-classes-copy.jar
     -> jetified-unity-classes-copy (unity-classes-copy.jar) and unity-classes.jar -> ...

410 lines of that, one per class. The task name is the useful part: checkReleaseDuplicateClasses means two artifacts on the classpath contain the same class, and the two module names on the first line are the two to reconcile. In a real project the pair is usually AdMob and In-App Purchase pulling different versions of Play Billing or play-services-basement through the External Dependency Manager, and the first thing to try is Assets → External Dependency Manager → Android Resolver → Delete Resolved Libraries, then a force resolve.

A repository in the wrong file

This one is specific to Unity 2022.2 and later, and it is worth knowing because the instructions shipped with a lot of ad and analytics SDKs are older than the change.

I added a JitPack-only dependency to Assets/Plugins/Android/mainTemplate.gradle and, in the same file, the repository it lives in:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
**DEPS**    implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
}

repositories {
    maven { url 'https://jitpack.io' }
}
* What went wrong:
Execution failed for task ':launcher:checkReleaseDuplicateClasses'.
> Could not resolve all files for configuration ':launcher:releaseRuntimeClasspath'.
   > Could not find com.github.PhilJay:MPAndroidChart:v3.1.0.
     Searched in the following locations:
       - https://dl.google.com/dl/android/maven2/com/github/PhilJay/...
       - https://repo.maven.apache.org/maven2/com/github/PhilJay/...

JitPack is not in that list. The repository block was read, parsed, and ignored, because Unity's generated settings.gradle sets:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)

Under PREFER_SETTINGS, Gradle discards repositories declared anywhere else. No warning appears — the only symptom is a search list that quietly lacks the one place the artifact exists.

The fix is the same declaration, moved. Copy settingsTemplate.gradle next to mainTemplate.gradle and put the repository in its dependencyResolutionManagement block:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        **ARTIFACTORYREPOSITORY**
        maven { url 'https://jitpack.io' }
        google()
        mavenCentral()

Same dependency, same project, next build succeeded. If a plugin's README tells you to edit mainTemplate.gradle and the artifact still cannot be found, the README predates this.

A mainTemplate.gradle from an older Unity

I dropped in the template Unity 2019 and 2020 generated — the one with minSdkVersion **MINSDKVERSION**, lintOptions and aaptOptions — which is exactly what a project carries when it has been upgraded and the file was committed years ago.

Gradle never ran. Unity 6 stopped at the prerequisites step:

Assets/Plugins/Android/mainTemplate.gradle file is missing the CMake arguments required
for GameActivity and Framepacing to work. To fix this, add "**DEFAULT_CONFIG_SETUP**"
inside the defaultConfig block. If not fixed, your build can fail.

UnityException: Error
mainTemplate.gradle file is using the old androidResources noCompress property definition
which does not include types defined by unityStreamingAssets constant.

That is a genuine improvement, and it changes the advice. The Unity 6 token names differ from the old ones — **MINSDK** rather than **MINSDKVERSION**, compileSdk rather than compileSdkVersion, lint rather than lintOptions — so if you have a custom template, the reliable move after an editor upgrade is to copy the current one out of

<editor>/PlaybackEngines/AndroidPlayer/Tools/GradleTemplates/mainTemplate.gradle

and reapply your handful of edits to it, rather than patching the old file forward.

The one that refused to break

I tried to reproduce the most-cited conflict of the past two years — the app declaring a lower minimum SDK than a library it depends on — by setting the project to API 22 and adding com.google.android.gms:play-services-ads:24.3.0, which needs 23. The build failed, but not where I expected:

Assets/Editor/BuildAndroid.cs(26,48): error CS0619: 'AndroidSdkVersions.AndroidApiLevel22'
is obsolete: 'Minimum supported Android API level is 25 (Android 7.1 Nougat).
Please use AndroidApiLevel25 or higher.'

Unity 6000.4 will not compile a script that names API 22. The floor is 25, above every current Play Services minimum, so the classic uses-sdk:minSdkVersion 22 cannot be smaller than version 23 declared in library is no longer reachable from a modern editor — it is still the top answer on threads dated July 2025, and still the right answer if you are on 2021 or 2022 LTS. With the floor left alone, that same AdMob dependency resolved and built with no changes at all.

A second one that would not break: putting <uses-sdk android:minSdkVersion="34" /> in the manifest of an .androidlib under Assets/Plugins/Android/. Unity regenerates that module's build.gradle with the player's minimum, so the manifest declaration is overridden and the build succeeds. Library minimums bite when they arrive as an AAR from a repository, not when they are sitting in your own project.

Two pieces of stale advice

Both come from a time when Unity used your machine's tooling. It does not.

The prerequisites step prints what it is actually going to use, and mine looked like this:

 JDK: '/Applications/Unity/Hub/Editor/6000.4.0f1/PlaybackEngines/AndroidPlayer/OpenJDK'
 Android SDK: '/Applications/Unity/Hub/Editor/6000.4.0f1/PlaybackEngines/AndroidPlayer/SDK'
 Android NDK: '/Applications/Unity/Hub/Editor/6000.4.0f1/PlaybackEngines/AndroidPlayer/NDK'
 Gradle: '.../AndroidPlayer/Tools/gradle' (Gradle Version: '8.13' Android Plugin Version: '8.10.0')

Installing a JDK will not help. Unity ran its own bundled OpenJDK 17.0.9. The system Java on this laptop is 25 and was never invoked. Pointing Unity at your Android SDK will not help eitherANDROID_HOME was set to ~/Library/Android/sdk in the environment dump Unity printed, and Unity used its own SDK anyway. Both answers were correct in 2019. They now send people to change something that has no effect, then conclude their install is corrupt.

What is actually breaking builds this year

The failures I could not stage on an empty project, from reports with dates on them:

  • Gradle running out of heap, which surfaces as something else entirely. One November 2025 report on Unity 6000.0.58f2 shows it as Execution failed for task ':launcher:signReleaseBundle' and java.lang.IllegalArgumentException: Self-suppression not permitted, fixed by raising Maximum JVM Heap Size from 4096 to 8192 in Preferences → External Tools. If your unity android build error mentions bundling or signing and the project is large, try this before anything else.
  • 16 KB page alignment. From 31 May 2026 Play stops accepting updates whose native libraries are 4 KB-aligned. NDK r27 and later emit aligned segments by default; the fix is an editor and NDK upgrade, not a Gradle setting.
  • Non-ASCII characters or spaces in the project path, still, still on Windows.
  • Two plugins, one library. The duplicate-class case above, arriving through the External Dependency Manager rather than by hand.

The takeaway

CommandInvokationFailure: Gradle build failed. is a category, in the way that "the car won't start" is a category. Reading the console top to bottom is how you end up believing it is about environment variables, and reinstalling the editor over a two-line Gradle problem.

Grep for * What went wrong:. Whatever it says next — a task name, a class name, a coordinate that could not be found — that is the thing to search, and it has an answer.

More posts

New writing when there is something worth saying. By email or by RSS, whichever you prefer.