permissions-dispatcher/PermissionsDispatcher · error · DuplicatedValueException

$value is duplicated in '${e.simpleString()}()' annotated wi

Error message

$value is duplicated in '${e.simpleString()}()' annotated with '@${annotation.simpleName}'

What it means

Thrown by checkDuplicatedValue in Validators.kt:41. When multiple methods in one annotated class request the exact same permission set via @NeedsPermission (or @OnPermissionDenied etc.), the processor detects the duplicate sorted permissionValue list and throws DuplicatedValueException. Each permission combination must map to a single method so generated dispatch code stays unambiguous.

Source

Thrown at processor/src/main/kotlin/permissions/dispatcher/processor/util/Validators.kt:41

    val type = element.asType()
    try {
        return units.first { type.isSubtypeOf(it.getTargetType()) }
    } catch (ex: NoSuchElementException) {
        throw WrongClassException(type)
    }
}

/**
 * Checks the elements in the provided list annotated with an annotation against duplicate values.
 * <p>
 * Raises an exception if any annotation value is found multiple times.
 */
fun <A : Annotation> checkDuplicatedValue(items: List<ExecutableElement>, annotationClass: Class<A>) {
    val allItems: HashSet<List<String>> = hashSetOf()
    items.forEach {
        val permissionValue = it.getAnnotation(annotationClass).permissionValue().sorted()
        if (allItems.contains(permissionValue)) {
            throw DuplicatedValueException(permissionValue, it, annotationClass)
        } else {
            allItems.add(permissionValue)
        }
    }
}

/**
 * Checks the elements in the provided list for elements.
 * <p>
 * Raises an exception if it doesn't contain any elements.
 */
fun <A : Annotation> checkNotEmpty(items: List<ExecutableElement>, rpe: RuntimePermissionsElement, annotationClass: Class<A>) {
    if (items.isEmpty()) {
        throw NoAnnotatedMethodsException(rpe, annotationClass)
    }
}

/**

View on GitHub (pinned to 74b532bf1f)

Solutions

  1. Remove or rename the duplicated annotation so only one method requests that exact permission set.
  2. Differentiate the methods by giving them distinct permission lists.
  3. Merge the duplicated methods into a single method called from both call sites.

Example fix

// before
@NeedsPermission(Manifest.permission.CAMERA)
fun showCamera() { }
@NeedsPermission(Manifest.permission.CAMERA)
fun openCamera() { }

// after
@NeedsPermission(Manifest.permission.CAMERA)
fun showCamera() { } // single method; call showCamera() from both places
Defensive patterns

Strategy: validation

Validate before calling

val perms = mapOf(
  "showCamera" to listOf(CAMERA),
  "openGallery" to listOf(READ_MEDIA_IMAGES)
)
require(perms.values.toSet().size == perms.size) { "duplicate permission sets" }

Try / catch

try {
    buildProject()
} catch (e: CompilationError) {
    logger.error("Duplicate @NeedsPermission values: give each method a unique permission set", e)
}

Prevention

When it happens

Trigger: Two methods in the same @RuntimePermissions class are annotated with identical permission arrays, e.g. two methods both @NeedsPermission(Manifest.permission.CAMERA), or same set in different order (order-insensitive because values are sorted before comparison).

Common situations: Copy-pasting an annotated method and forgetting to change its permissions; two overloads annotated with the same permissions; merging branches that each added a method for the same permission.

Related errors


AI-assisted analysis of permissions-dispatcher/PermissionsDispatcher@74b532bf1f (2026-09-08). Data as JSON: /api/errors/f3af517a2db3a648. Report an issue: GitHub.