permissions-dispatcher/PermissionsDispatcher · error · SpecialPermissionsWithNeverAskAgainException
'@NeverAskAgain' annotated method never being called with 'W
Error message
'@NeverAskAgain' annotated method never being called with 'WRITE_SETTINGS' or 'SYSTEM_ALERT_WINDOW' permission.
What it means
PermissionsDispatcher throws this at compile time when a method annotated with @OnNeverAskAgain includes WRITE_SETTINGS or SYSTEM_ALERT_WINDOW in its permission list. These special permissions are granted through Settings screens rather than the runtime dialog, so the runtime 'never ask again' callback can never fire for them; the annotation is meaningless and flagged as an error.
Source
Thrown at processor/src/main/kotlin/permissions/dispatcher/processor/util/Validators.kt:129
fun <A : Annotation> checkMixPermissionType(items: List<ExecutableElement>, annotationClass: Class<A>) {
items.forEach {
val permissionValue = it.getAnnotation(annotationClass).permissionValue()
if (permissionValue.size > 1) {
if (permissionValue.contains(WRITE_SETTINGS)) {
throw MixPermissionTypeException(it, WRITE_SETTINGS)
} else if (permissionValue.contains(SYSTEM_ALERT_WINDOW)) {
throw MixPermissionTypeException(it, SYSTEM_ALERT_WINDOW)
}
}
}
}
fun checkSpecialPermissionsWithNeverAskAgain(items: List<ExecutableElement>, annotationClass: Class<OnNeverAskAgain> = OnNeverAskAgain::class.java) {
items.forEach {
val permissionValue = it.getAnnotation(annotationClass).permissionValue()
if (permissionValue.contains(WRITE_SETTINGS) || permissionValue.contains(SYSTEM_ALERT_WINDOW)) {
throw SpecialPermissionsWithNeverAskAgainException()
}
}
}
fun checkDuplicatedMethodName(items: List<ExecutableElement>) {
items.forEach { item ->
items.firstOrNull { it != item && it.simpleName == item.simpleName }?.let {
throw DuplicatedMethodNameException(item)
}
}
}
View on GitHub (pinned to 74b532bf1f)
Solutions
- Remove WRITE_SETTINGS and SYSTEM_ALERT_WINDOW from the @NeverAskAgain method's permission list
- Handle special-permission 'denied' states manually by checking Settings.canDrawOverlays() or Settings.System.canWrite() and directing the user to the Settings screen
- Keep a separate @NeedsPermission method for the special permission with only @OnShowRationale/@OnPermissionDenied handling, no @NeverAskAgain
Example fix
// before
@NeverAskAgain(Manifest.permission.CAMERA, Manifest.permission.WRITE_SETTINGS)
fun onCameraNeverAskAgain() { }
// after
@NeverAskAgain(Manifest.permission.CAMERA)
fun onCameraNeverAskAgain() { } Defensive patterns
Strategy: validation
Validate before calling
val specialPerms = setOf(Manifest.permission.WRITE_SETTINGS, Manifest.permission.SYSTEM_ALERT_WINDOW)
// ensure @NeverAskAgain callback lists never include special permissions
val neverAskAgainPerms: Array<String> = callbackPermissions
require(neverAskAgainPerms.none { it in specialPerms }) {
"@NeverAskAgain cannot be used with WRITE_SETTINGS or SYSTEM_ALERT_WINDOW"
} Type guard
fun isSpecialPermission(p: String) =
p == Manifest.permission.WRITE_SETTINGS || p == Manifest.permission.SYSTEM_ALERT_WINDOW
val Array<String>.containsSpecial: Boolean get() = any { isSpecialPermission(it) } Prevention
- Never list WRITE_SETTINGS or SYSTEM_ALERT_WINDOW in @NeverAskAgain callbacks; they never trigger it
- Detect special-permission denial manually via Settings.canDrawOverlays() / Settings.System.canWrite()
- Use identical permission arrays only across @NeedsPermission and @OnPermissionDenied, filtering special ones from callback annotations
When it happens
Trigger: Annotating a callback method with @NeverAskAgain and listing Manifest.permission.WRITE_SETTINGS or Manifest.permission.SYSTEM_ALERT_WINDOW among its permissions, then running the annotation processor (Kotlin kapt / Java APT build).
Common situations: Developers mirror the same permission array across @NeedsPermission/@OnNeverAskAgain/@OnPermissionDenied callbacks, accidentally carrying WRITE_SETTINGS or SYSTEM_ALERT_WINDOW into the @NeverAskAgain method.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- '${TypeName.get(type)}' can't be annotated with '@RuntimePer
- $value is duplicated in '${e.simpleString()}()' annotated wi
- Annotated class '${rpe.inputClassName}' doesn't have any met
- Method '${e.simpleString()}()' annotated with '@${annotation
- Method '${e.simpleString()}()' must specify return type 'voi
AI-assisted analysis of permissions-dispatcher/PermissionsDispatcher@74b532bf1f (2026-09-08).
Data as JSON: /api/errors/28bfa4ddda0efaa9.
Report an issue: GitHub.