Mobile Development Bug Report

Flutter Android build fails with Namespace not specified after AGP 8 upgrade

A new Android Studio with JDK 21 forced a Gradle upgrade, the Gradle upgrade pulled in AGP 8, and AGP 8 rejected two old Flutter plugins with Namespace not specified. How we untangled the Gradle, AGP, Kotlin and JDK chain.

Daniel Tran Daniel Tran @fun_dev · November 19, 2024 · 8 min read · 13 Views
Banner: Flutter Android build failing with Namespace not specified after an AGP 8 upgrade

It started with a laptop refresh. A developer on our grocery delivery app set up a new MacBook with the current Android Studio, Ladybug, and the Flutter app that built fine on everyone else's machine failed immediately:

FAILURE: Build failed with an exception.

* What went wrong:
Could not open cp_settings generic class cache for settings file '.../android/settings.gradle'.
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 65

Class file major version 65 is Java 21. Ladybug bundles JDK 21, Flutter uses Android Studio's JDK by default, and our project was on Gradle 7.6.3, which cannot run on Java 21. The Flutter docs suggest upgrading Gradle, and Android Studio's AGP Upgrade Assistant offered to go all the way. We accepted Gradle 8.10.2 and AGP 8.7.2. The next build failed differently:

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':store_hours_native'.
> Could not create an instance of type com.android.build.api.variant.impl.LibraryVariantBuilderImpl.
   > Namespace not specified. Specify a namespace in the module's build file:
     /Users/dev/.pub-cache/git/store_hours_native-1f0c.../android/build.gradle.
     See https://d.android.com/r/tools/upgrade-assistant/set-namespace for information about setting the namespace.

Reproducing it

The failure is deterministic once the versions line up:

  1. Use AGP 8.0 or newer in android/settings.gradle.
  2. Depend on a Flutter plugin whose android/build.gradle has no namespace in its android {} block and relies on the package attribute in its AndroidManifest.xml.
  3. Run flutter build apk --debug.

What we ruled out

  • Our own app module. The app's android/app/build.gradle already had namespace "com.example.grocer"; Flutter's templates have included it for a while. The error named a plugin project, not :app.
  • A corrupted pub cache. flutter pub cache repair and a clean checkout changed nothing.
  • Flutter itself. Flutter 3.24.4 supports AGP 8 and Gradle 8; a fresh flutter create project with the same versions built fine.

Root cause

AGP 8 removed support for reading a library's namespace from the package attribute in its manifest. Every Android module must declare namespace in its Gradle file. Two of our plugins predated that requirement: store_hours_native, an in-house plugin we had not touched in three years, and an unmaintained third-party PDF viewer pinned to a 2021 release. Upgrading the build toolchain was correct; our plugins were simply not ready for it.

What is happening under the hood

The namespace is the Java/Kotlin package used for the generated R and BuildConfig classes. Before AGP 7.3, it came from package="..." in AndroidManifest.xml. AGP 7.3 introduced namespace in the DSL as the preferred source, and AGP 8.0 made it mandatory. Flutter plugins are Gradle library modules, so each plugin's Android project is subject to the same rule as your app.

The upgrade was really a chain of constraints, and each link forced the next:

Link Constraint we hit
Android Studio Ladybug Bundles JDK 21
JDK 21 Gradle 8.5 or newer needed to run on it
AGP 8.7 Requires Gradle 8.9+ and JDK 17+
AGP 8 Every module needs namespace
Kotlin Gradle plugin with JDK 17 Java and Kotlin JVM targets must match

That last row produced our third error, from the PDF plugin once the namespace was fixed:

Execution failed for task ':pdf_view_legacy:compileDebugKotlin'.
> Inconsistent JVM-target compatibility detected for tasks 'compileDebugJavaWithJavac' (1.8) and 'compileDebugKotlin' (17).

The plugin set compileOptions to Java 8 but did not set kotlinOptions.jvmTarget, so Kotlin defaulted to the toolchain version.

Environment

Component Version / value
macOS 15.1, Apple Silicon
IDE Android Studio Ladybug 2024.2.1 (bundled JDK 21)
Flutter 3.24.4 stable, Dart 3.5.4
Gradle 7.6.3 before, 8.10.2 after
AGP 7.3.0 before, 8.7.2 after
Kotlin Gradle plugin 1.7.10 before, 1.9.25 after
Problem plugins in-house store_hours_native (git dependency), pdf_view_legacy 1.2.0

Minimal reproduction

// A plugin's android/build.gradle that worked on AGP 7 and fails on AGP 8
apply plugin: 'com.android.library'

android {
    compileSdkVersion 31
    defaultConfig { minSdkVersion 21 }
    // no namespace: AGP 8 refuses to configure this module
}
<!-- android/src/main/AndroidManifest.xml of the same plugin -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.storehours" />

Who is affected

Any Flutter app that moves to AGP 8 or newer while depending on a plugin that has not declared a namespace. Most popular plugins fixed this in 2023, so the usual culprits are in-house plugins, forks, and discontinued packages. React Native and native Android projects with old library modules hit the same error.

The fix

1. Pin the toolchain deliberately in Flutter's declarative plugin block, instead of leaving it to whatever the IDE suggests:

// android/settings.gradle
plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "8.7.2" apply false
    id "org.jetbrains.kotlin.android" version "1.9.25" apply false
}
# android/gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip

2. Fix the in-house plugin properly. We own it, so we added the namespace, removed the manifest package attribute, and aligned JVM targets:

  android {
+     namespace "com.example.storehours"
      compileSdk 34
      defaultConfig { minSdk 21 }
+     compileOptions {
+         sourceCompatibility JavaVersion.VERSION_17
+         targetCompatibility JavaVersion.VERSION_17
+     }
+     kotlinOptions { jvmTarget = "17" }
  }
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-     package="com.example.storehours" />
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />

We tagged a new version of the plugin's git repository and bumped the ref in pubspec.yaml.

3. Replace or fork the abandoned plugin. We checked pub.dev for a maintained alternative that covered our use (viewing a receipt PDF) and migrated two screens to it. Had there been none, we would have forked it into our organisation and used a git dependency with the same two-line fix:

# pubspec.yaml (fork option, not what we shipped)
dependency_overrides:
  pdf_view_legacy:
    git:
      url: https://github.com/example-org/pdf_view_legacy.git
      ref: agp8-namespace

4. Make the JDK explicit so every machine and CI agent uses the same one:

flutter config --jdk-dir="/Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home"
flutter doctor -v | grep -i "java version"

We chose JDK 17 for now because it is what our CI image ships, and AGP 8.7 with Gradle 8.10 is also fine on 21.

Alternatives and trade-offs

  • Inject a namespace from the root build.gradle. A widely shared snippet sets android.namespace for every library subproject in an afterEvaluate hook. It works, but it hides plugins that need attention, can pick a wrong namespace, and must be placed before Flutter's evaluationDependsOn(":app") line or Gradle complains that the project is already evaluated. We treat it as an emergency measure only.
  • Point Flutter at an older JDK and stay on AGP 7. This unblocks a laptop in a minute and was our first workaround. It only postpones the migration; Play's target SDK requirements will eventually need newer AGP versions anyway.
  • Upgrade Flutter first. Newer Flutter releases bump template versions and warn about outdated Gradle, AGP and Kotlin versions, which makes the chain visible earlier. It does not fix third-party plugins.

How to test the fix

flutter clean
flutter pub get
cd android && ./gradlew --version && cd ..
flutter build apk --release
flutter build appbundle --release

We ran the same commands on a CI agent with JDK 17 and on the Ladybug laptop with its bundled JDK 21. Both produced identical versionCode and app sizes within a few kilobytes, and our integration tests passed on a Pixel 7 emulator.

Confirming in production

  • The release built from the upgraded toolchain went through Play's pre-launch report with no new crashes or warnings.
  • Crash-free users on Android stayed at 99.6 percent after a full rollout.
  • The receipt viewer, the screen most at risk because we replaced its plugin, had no new crash clusters in Crashlytics and its open rate matched the prior version.

Regression guard

  • CI now fails the build when flutter doctor -v reports a Java version other than the one pinned in our .tool-versions.
  • A script in CI lists every plugin's android/build.gradle from .flutter-plugins-dependencies and fails if any lacks a namespace line, so the next AGP bump does not surprise us.
  • Dependabot opens weekly pull requests for pub packages, and a monthly task reviews any plugin marked as discontinued on pub.dev.

Security implications

None beyond the bug itself. Upgrading AGP and Gradle picks up security fixes in the build toolchain, which is a positive side effect. Replacing an unmaintained plugin also removes native code nobody was reviewing. If you fork a plugin, own its updates; a fork that is never rebased becomes the next abandoned dependency.

Prevention checklist

  • Upgrade Gradle, AGP, Kotlin and the JDK together, using the official compatibility tables.
  • Pin the JDK used by Flutter with flutter config --jdk-dir on every machine and in CI.
  • Check plugins for namespace support before raising AGP.
  • Retire discontinued plugins early instead of pinning them.
  • Do not leave global namespace-injection hacks in the root build file after the emergency.

References

Add a label

#android #flutter #gradle #agp #kotlin
Written by
Daniel Tran @fun_dev

I have spent most of my career on the side of software that pages people at night: payment integrations, web platforms with real traffic, CI/CD pipelines, Kubernetes clusters and the databases underneath them. Somewhere along the way I started keeping a notebook of every bug that cost more than an hour — the exact error, what we ruled out, the real root cause and how we proved the fix. Hibug is where those notes live. Every post follows the same path: Investigate, Explain, Fix, Verify — with the environment spelled out, the fix tested, and the security angle checked.

Advertisement

Comments

0

No comments yet. Be the first to comment!

Sign In to post a comment.