{"record":{"id":"28bfa4ddda0efaa9","repo":"permissions-dispatcher/PermissionsDispatcher","slug":"neveraskagain-annotated-method-never-being-call","errorCode":null,"errorMessage":"'@NeverAskAgain' annotated method never being called with 'WRITE_SETTINGS' or 'SYSTEM_ALERT_WINDOW' permission.","messagePattern":"'@NeverAskAgain' annotated method never being called with 'WRITE_SETTINGS' or 'SYSTEM_ALERT_WINDOW' permission\\.","errorType":"validation","errorClass":"SpecialPermissionsWithNeverAskAgainException","httpStatus":null,"severity":"error","filePath":"processor/src/main/kotlin/permissions/dispatcher/processor/util/Validators.kt","lineNumber":129,"sourceCode":"\nfun <A : Annotation> checkMixPermissionType(items: List<ExecutableElement>, annotationClass: Class<A>) {\n    items.forEach {\n        val permissionValue = it.getAnnotation(annotationClass).permissionValue()\n        if (permissionValue.size > 1) {\n            if (permissionValue.contains(WRITE_SETTINGS)) {\n                throw MixPermissionTypeException(it, WRITE_SETTINGS)\n            } else if (permissionValue.contains(SYSTEM_ALERT_WINDOW)) {\n                throw MixPermissionTypeException(it, SYSTEM_ALERT_WINDOW)\n            }\n        }\n    }\n}\n\nfun checkSpecialPermissionsWithNeverAskAgain(items: List<ExecutableElement>, annotationClass: Class<OnNeverAskAgain> = OnNeverAskAgain::class.java) {\n    items.forEach {\n        val permissionValue = it.getAnnotation(annotationClass).permissionValue()\n        if (permissionValue.contains(WRITE_SETTINGS) || permissionValue.contains(SYSTEM_ALERT_WINDOW)) {\n            throw SpecialPermissionsWithNeverAskAgainException()\n        }\n    }\n}\n\nfun checkDuplicatedMethodName(items: List<ExecutableElement>) {\n    items.forEach { item ->\n        items.firstOrNull { it != item && it.simpleName == item.simpleName }?.let {\n            throw DuplicatedMethodNameException(item)\n        }\n    }\n}\n","sourceCodeStart":111,"sourceCodeEnd":141,"githubUrl":"https://github.com/permissions-dispatcher/PermissionsDispatcher/blob/74b532bf1fe430fb12ad8414b80399f2d7cfc954/processor/src/main/kotlin/permissions/dispatcher/processor/util/Validators.kt#L111-L141","documentation":"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.","triggerScenarios":"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).","commonSituations":"Developers mirror the same permission array across @NeedsPermission/@OnNeverAskAgain/@OnPermissionDenied callbacks, accidentally carrying WRITE_SETTINGS or SYSTEM_ALERT_WINDOW into the @NeverAskAgain method.","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"],"exampleFix":"// before\n@NeverAskAgain(Manifest.permission.CAMERA, Manifest.permission.WRITE_SETTINGS)\nfun onCameraNeverAskAgain() { }\n\n// after\n@NeverAskAgain(Manifest.permission.CAMERA)\nfun onCameraNeverAskAgain() { }","handlingStrategy":"validation","validationCode":"val specialPerms = setOf(Manifest.permission.WRITE_SETTINGS, Manifest.permission.SYSTEM_ALERT_WINDOW)\n// ensure @NeverAskAgain callback lists never include special permissions\nval neverAskAgainPerms: Array<String> = callbackPermissions\nrequire(neverAskAgainPerms.none { it in specialPerms }) {\n    \"@NeverAskAgain cannot be used with WRITE_SETTINGS or SYSTEM_ALERT_WINDOW\"\n}","typeGuard":"fun isSpecialPermission(p: String) =\n    p == Manifest.permission.WRITE_SETTINGS || p == Manifest.permission.SYSTEM_ALERT_WINDOW\n\nval Array<String>.containsSpecial: Boolean get() = any { isSpecialPermission(it) }","tryCatchPattern":null,"preventionTips":["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"],"tags":["kotlin","annotation-processor","android-permissions","neveraskagain"],"backgroundTag":"unsupported-operation","analyzedSha":"74b532bf1fe430fb12ad8414b80399f2d7cfc954","analyzedAt":"2026-09-08T21:44:58.380Z","contentChangedAt":"2026-09-08T21:44:58.380Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}