Skip to content

Troubleshooting builds and models

Each section below names the failure as you see it, then the fix. For why a model can be unavailable, see When on-device AI is unavailable; for requirements, see Compatibility and requirements.

Capacitor build failures

Your app needs settings that a freshly generated Capacitor app does not set. The plugin README lists them in Host Project Requirements (opens in new tab) (pinned to v1.2.0).

Version: @independo/capacitor-inderun 1.2.0, which includes IndeRun 0.4.0. The examples on this page are type-checked against it (2026-11-10).

Android: compile against version 37

requires libraries and applications that depend on it to compile against version 37 or later

Set compileSdkVersion to 37 in android/variables.gradle. Also set minSdkVersion to 26; if it is lower, the failure is a manifest merger conflict.

Android: Android Gradle plugin 9.1

requires Android Gradle plugin 9.1.0 or higher

Use Android Gradle plugin 9.1.0 or newer, with Gradle 9.x.

Android: Kotlin metadata 2.4.0 versus 2.2.0

Module was compiled with an incompatible version of Kotlin … metadata is 2.4.0, expected version is 2.2.0

The IndeRun artifacts carry Kotlin 2.4.x metadata, which the Kotlin plugin that AGP brings by default cannot read. Add org.jetbrains.kotlin:kotlin-gradle-plugin:2.4.10 to your app’s buildscript classpath in android/build.gradle. The README has the snippet.

Android: proguard-android.txt

AGP 9 rejects getDefaultProguardFile('proguard-android.txt'). Switch to proguard-android-optimize.txt. This is in the same README section.

iOS: minimum platform version 16.0

requires minimum platform version 16.0 for the iOS platform, but this target supports 15.0

Set IPHONEOS_DEPLOYMENT_TARGET to 16.0 in your Xcode project before you run npx cap sync ios. The Capacitor CLI reads that value to generate CapApp-SPM/Package.swift. If the manifest was already generated with the default, set the target and sync again.

The system model is unavailable

A build that succeeds can still have no model to run. checkCapabilities() reports available: false with a reason from the platform; see Check the device.

Platform Typical causes What to do
iOS Device not eligible, Apple Intelligence turned off, model not ready yet Check Apple’s availability reasons (opens in new tab), turn Apple Intelligence on, wait for the model to be ready. Test on a physical device.
Android AICore does not support the device, Gemini Nano not downloaded, app not in the foreground, per-app quota See the ML Kit GenAI overview (opens in new tab). Test on a physical device.
Web Not desktop Chrome, hardware or storage requirements unmet, model download pending See Chrome’s Prompt API (opens in new tab).

This is not a bug in your integration. If the app should still work, allow a cloud route that you configure yourself, as described in When on-device AI is unavailable.

Refused: no eligible provider

With local_required and no eligible on-device model, the SDK’s run() rejects with an IndeRunError whose errorClass is "CapabilityMismatch". In the Capacitor plugin, the bridge forwards details from the native side in its source; this has not been verified on a device. The message reads like:

No eligible local provider found for task 'text_to_text'.

details.rejectedProviders lists each registered provider with the reason codes it was skipped for, as [{ providerId, reasons: [{ code, … }] }]. Common codes are capability_unavailable (the model isn’t available on this device), privacy_constraint and cloud_constraint (your rule excluded it), task_not_supported, not_ready and offline. The full list is in Placement constraints (opens in new tab). There is no no_eligible_provider code.

A refusal means no provider ran. It is the intended result of your rule on this device. provenance.routeRejections appears only on a successful result, where it lists providers skipped on the way. The Capacitor source forwards details from the native side; confirm the exact shape in your own run, as it hasn’t been verified on a device for this page. See Read the outcome.

Refused, execution error or bridge fault

The plugin’s README names three failure surfaces for a streaming run in its Error Handling (opens in new tab) section (v1.2.0):

Surface When How it reaches you
Rejection Request validation, or no streaming-capable provider stream() rejects with an IndeRunError
Terminal error event A provider failed, or the whole planned chain did Not a rejection: for await completes normally and the last event is terminal with payload.outcome === "error"
Bridge fault The transport lost or could not encode events events throws an Internal IndeRunError

So a run that fails after it started ends your loop normally. Branch on event.payload.outcome to tell completion from failure from cancellation. For run(), errors conform to IndeRunError; branch on error.errorClass. A routing refusal is a CapabilityMismatch rejection (see above); it is not an execution error, because nothing executed.

Speech problems

See Read text aloud for the model behind these.

voice_locality_unknown on Android

A local_required request was refused with details.code: "voice_locality_unknown". The installed speech engine lists no voices, so there is no network flag to check and IndeRun can’t confirm the speech stays on the device. Install or switch to a speech engine that lists its voices (Google TTS is the one that has been run), or make the request without local_required. This is an error detail, not a rejection code.

Missing locale or voice

A required voice that isn’t installed is refused with voice_not_available, and a locale no voice serves with locale_not_supported. IndeRun doesn’t substitute another voice. Call listVoices({ locale }) to see what the device offers, install the voice in the system settings, or prefer the voice instead of requiring it. On Android a required gender also ends in voice_not_available, because the platform exposes no gender.

local_required speech refused in a browser

Always expected. A browser can’t confirm that a voice stays on the device, so local_required speech is refused on the web. Use the default rule if network voices are acceptable, or run the request in the iOS or Android app.