This error has a particularly unhelpful personality: it names an internal Android build-model field instead of the file or plugin that left it empty. Around the Android Studio 3.5 generation, it often appeared after an IDE/Android Gradle Plugin change met outputs created by an older build. Cleaning helped many projects—but it was never proof that every occurrence had the same cause.

Fast recovery ladder

  1. Commit or otherwise back up build scripts, wrapper files, and working source changes.

  2. Reproduce the failure with the checked-in Gradle Wrapper and one explicit variant task.

  3. Record Gradle, JVM, operating system, Android Studio, AGP, Kotlin, and relevant third-party plugin versions.

  4. Stop wrapper daemons and perform a project-local clean.

  5. Rebuild with a stack trace and diagnostic logging.

  6. If it still fails, verify the official AGP–Gradle–JDK–Studio compatibility chain.

  7. Review custom/third-party plugins that inspect variants or APK outputs.

  8. Upgrade with the AGP Upgrade Assistant in a dedicated branch, one major step at a time.

1. Preserve evidence before changing versions

Android project root containing gradlewbash
git status --short
git diff -- build.gradle build.gradle.kts settings.gradle settings.gradle.kts \
    gradle/libs.versions.toml gradle/wrapper/gradle-wrapper.properties
./gradlew --version
------------------------------------------------------------
Gradle ...
------------------------------------------------------------
JVM: ...
OS: ...

Version evidence makes the failure reproducible

  • git status and git diff expose an IDE-assisted version edit that was not reviewed or committed.

  • The wrapper reports the Gradle distribution and JVM actually used by that terminal invocation.

  • Some listed files may not exist in every project; inspect whichever Groovy/Kotlin DSL and version-catalog files the repository uses.

  • Do not share repository credentials, signing properties, environment secrets, or private dependency URLs in logs.

  • Record the exact failing task and variant, such as :app:assembleDebug, rather than saying only “Build Project failed.”

2. Reproduce the narrowest failing task

Android project rootbash
./gradlew :app:assembleDebug --stacktrace --info --no-build-cache
FAILURE: Build failed with an exception.
...
buildOutput.apkData must not be null

A focused build reduces noise

  • Replace app and Debug with the real module and variant.

  • --stacktrace preserves the exception chain; read for the first project or third-party plugin frame before the final wrapper exception.

  • --info adds task and plugin context without the volume of --debug.

  • --no-build-cache avoids reusing task outputs for this invocation but does not delete global caches.

  • A failure during configuration or clean points away from a stale APK output and toward build logic or compatibility.

3. Stop daemons and clean project outputs

Android project rootbash
./gradlew --stop
./gradlew clean
./gradlew :app:assembleDebug --stacktrace --info
Stopping Daemon(s)
...
BUILD SUCCESSFUL

Risk level: caution. Review the command before running it.

Clean is targeted and reversible

  • --stop asks daemons for this wrapper version to terminate; it is useful when IDE and shell builds retained different state.

  • clean deletes outputs declared by project clean tasks, so it is marked caution even though those artifacts should be reproducible.

  • The following build recreates task outputs with the current plugin model.

  • A clean build is slower and should be a diagnostic step, not a ritual before every build.

  • If clean resolves the issue repeatedly, investigate an incorrect or undeclared task input/output rather than institutionalizing constant cleaning.

When clean itself cannot run

Close Android Studio, confirm no Gradle process is using the project, and remove only generated project-local directories whose purpose you understand—for example the root .gradle/, root build/, and affected module build/. These are different from source, the gradle/ wrapper directory, signing files, and the user-wide Gradle home.

  • Back up first and resolve the exact project path before deletion.

  • Never delete the repository root, gradle/wrapper/, source folders, or an unresolved wildcard target.

  • Do not start with the entire user-wide ~/.gradle cache; it affects every project and forces large downloads.

  • Android Studio’s normal clean/rebuild actions are preferable when they can execute the project’s tasks.

  • After project-local removal, reproduce from the wrapper in a terminal before reopening the IDE.

4. Verify the compatibility chain

Android builds are a version graph, not a single “Gradle version.” The Android Gradle Plugin requires a compatible Gradle wrapper; the AGP release has a supported JDK range; Android Studio must support that AGP generation; Kotlin and third-party plugins must also understand the selected Gradle/AGP APIs.

  • Find the AGP version in the plugins block, root build script, version catalog, or legacy buildscript classpath.

  • Find the Gradle distribution in gradle/wrapper/gradle-wrapper.properties.

  • Compare that pair with Android’s current AGP compatibility table, including the table’s older-version section for historical projects.

  • Check the Gradle JVM selected by Android Studio against the JVM shown by ./gradlew --version.

  • Review the chosen AGP release notes for required JDK, SDK, DSL, namespace, packaging, and variant API migrations.

  • Pin versions in source control; do not allow different developers to accept unrelated IDE prompts independently.

5. Compare Android Studio and terminal builds

  • Use the same checked-out commit and wrapper task in both environments.

  • Inspect Android Studio’s Gradle JDK rather than assuming it follows shell JAVA_HOME.

  • Compare environment variables, local SDK path, Gradle properties, proxy settings, and credentials providers.

  • Disable offline mode if required artifacts are not already cached.

  • If CLI succeeds and IDE fails, resync the project and inspect IDE logs/model import before changing application code.

  • If both fail identically, the stack trace and build scripts are stronger evidence than IDE cache invalidation.

6. Audit plugins that consume APK outputs

Old crash reporting, distribution, renaming, signing, analytics, and custom build plugins sometimes reached into internal AGP variant/output objects. Those APIs changed substantially. An internal apkData null failure after a clean build can indicate a plugin expecting an output model the selected AGP no longer supplies.

  • Read the first non-Gradle/non-AGP stack frame and identify its owning plugin.

  • Check that plugin’s release notes for the exact AGP and Gradle versions.

  • Temporarily disable one suspected plugin in a branch and rerun the same task.

  • Replace internal variant/output access with supported Android Components APIs when maintaining custom plugins.

  • Check every application variant, split, flavor, and output-renaming customization; a plugin may fail only on one shape.

  • Do not resolve the crash by forcing a nullable internal field or copying a random build script from another AGP generation.

7. Upgrade deliberately, not all at once

  1. Create a clean upgrade branch with a passing baseline build and tests.

  2. Choose an Android Studio release that can open the current AGP generation or install an appropriate historical version for the first step.

  3. Run the AGP Upgrade Assistant and review each proposed change.

  4. Move through required major versions and their migrations instead of leaping across years of removed APIs blindly.

  5. Update the wrapper, AGP, JDK, Kotlin, and third-party plugins in a documented compatible sequence.

  6. Build all variants, run unit/instrumentation tests, inspect APK/AAB contents and signing, and compare CI.

  7. Commit each coherent upgrade step so regressions can be bisected or reverted.

Commands that do not solve this by default

  • `--refresh-dependencies`: use it for dependency-resolution or changing-module cache problems, not stale local APK metadata without evidence.

  • Deleting all of `~/.gradle`: broad, disruptive, and rarely the first justified action.

  • `--rerun-tasks`: useful to test task up-to-date behavior, but it cannot repair incompatible build logic.

  • Invalidate IDE caches: may help an IDE index/model problem, but does not change a reproducible wrapper failure.

  • Latest-everything upgrade: can replace one historical error with many migration failures and erase the original evidence.

  • Downgrading one component alone: an arbitrary AGP, Gradle, JDK, or Studio downgrade can create an unsupported combination.

Decision map

  • Clean rebuild succeeds once: stale project output/model state was likely; verify repeatability and commit no generated directories.

  • Failure occurs before any task runs: inspect settings/build-script syntax, plugin application, JVM, and compatibility.

  • Only one variant fails: inspect flavor/build-type configuration, splits, output naming, and variant-aware plugins.

  • Only Android Studio fails: align the IDE Gradle JDK and wrapper, resync, and inspect model-import logs.

  • Only CI fails: compare JDK, SDK packages, filesystem case, environment, credentials, wrapper checksum, and clean-checkout behavior.

  • Stack trace names a third-party plugin: update, configure, replace, or isolate it using its documented compatibility.

  • Modern AGP still prints `apkData`: suspect obsolete plugin code or an incomplete multi-generation migration; capture a minimal reproducible project.

Build-health practices that prevent recurrence

  • Keep generated build/ directories out of version control.

  • Commit and verify Wrapper files and pin plugin versions.

  • Use the same wrapper task and JDK policy locally and in CI.

  • Build from a clean checkout regularly so undeclared local state cannot become required.

  • Avoid dynamic dependency/plugin versions in reproducible production builds.

  • Treat build-cache correctness problems as bugs in task inputs/outputs, not reasons for permanent clean.

  • Document supported Android Studio, JDK, SDK, NDK, AGP, Gradle, and Kotlin versions.

  • Preserve full stack traces and build scans/logs without secrets when escalating a toolchain bug.

Current and historical scope

The literal buildOutput.apkData must not be null report is most strongly associated with the Android Studio 3.5/AGP 3.x era. For an unchanged historical project, a project-local clean and a compatible historical toolchain may restore the build. For maintained software, plan a supported upgrade rather than freezing an internet-facing supply chain indefinitely.

Authoritative references