quarkusio/quarkus · error · java.lang.RuntimeException

No '%s' formal parameter name matches '%s' Permission %s par

Error message

No '%s' formal parameter name matches '%s' Permission %s parameter name '%s'

What it means

In the reverse direction, Quarkus iterates the parameters of the constructor (or Quarkus permission checker method) and must find a matching formal parameter on the secured method. When a constructor/checker parameter (e.g. of the custom Permission class) has no corresponding named formal parameter on the secured method, match.methodParamIdx() == -1 and this RuntimeException is thrown, naming the secured method, the match target, whether it was a checker or constructor, and the unmatched parameter name.

Source

Thrown at extensions/security/deployment/src/main/java/io/quarkus/security/deployment/PermissionSecurityChecks.java:1439

            private static SecMethodAndPermCtorIdx[] matchPermCtorParamIdxBasedOnNameMatch(MethodInfo securedMethod,
                    MethodInfo constructor, boolean passActionsToConstructor, String[] requiredMethodParams,
                    String[] requiredParamsRemainder, IndexView index, boolean isQuarkusPermission,
                    MethodInfo permissionChecker) {
                // assign method param to each constructor param; it's not one-to-one function (AKA injection)
                final int nonMethodParams = (passActionsToConstructor ? 2 : 1);
                final var matches = new SecMethodAndPermCtorIdx[constructor.parametersCount() - nonMethodParams];
                for (int i = nonMethodParams; i < constructor.parametersCount(); i++) {
                    // find index for exact name match between constructor and method param
                    var match = findSecuredMethodParamIndex(securedMethod, constructor, i,
                            requiredParamsRemainder, requiredMethodParams, nonMethodParams, index);
                    matches[i - nonMethodParams] = match;
                    if (match.methodParamIdx() == -1) {
                        final String constructorParamName = constructor.parameterName(i);
                        final String matchTarget = isQuarkusPermission
                                ? PermissionSecurityChecksBuilder.toString(permissionChecker)
                                : constructor.declaringClass().name().toString();
                        throw new RuntimeException(String.format(
                                "No '%s' formal parameter name matches '%s' Permission %s parameter name '%s'",
                                PermissionSecurityChecksBuilder.toString(securedMethod), matchTarget,
                                isQuarkusPermission ? "checker" : "constructor", constructorParamName));
                    }
                }
                return matches;
            }

            private static SecMethodAndPermCtorIdx findSecuredMethodParamIndex(MethodInfo securedMethod, MethodInfo constructor,
                    int constructorIx, String[] requiredParamsRemainder, String[] requiredParams, int nonMethodParams,
                    IndexView index) {
                final String constructorParamName = constructor.parameterName(constructorIx);
                final int constructorParamIdx = constructorIx - nonMethodParams;

                if (requiredParams != null && requiredParams.length != 0) {
                    // user specified explicitly parameter names with @PermissionsAllowed(params = "some.name")
                    for (int i = 0; i < securedMethod.parametersCount(); i++) {
                        var methodParamName = securedMethod.parameterName(i);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add the missing name to @PermissionsAllowed params (or as a secured method parameter) so the constructor/checker parameter can be matched, e.g. params = { "owner" }.
  2. Remove the extra parameter from the custom Permission constructor if it is not needed.
  3. Ensure the secured method is compiled with -parameters so formal parameter names are available for matching.

Example fix

// before
@PermissionsAllowed(value = "get", clazz = GetPermission.class) // GetPermission(name, ownerId) — ownerId unmatched
public Document get(Long id) { ... }

// after
@PermissionsAllowed(value = "get", clazz = GetPermission.class, params = { "id" })
public Document get(Long id) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// Every custom Permission constructor parameter must be satisfied via @PermissionsAllowed params:
// for (var p : permissionConstructor.getParameters())
//     assert annotationParams.contains(p.getName()) : p.getName() + " unmatched on secured method";

Prevention

When it happens

Trigger: A custom Permission class constructor declares a parameter (e.g. 'owner') that no @PermissionsAllowed params entry or secured-method parameter satisfies, so the permission cannot be built with values from the secured method invocation.

Common situations: Custom Permission constructor extended with a new parameter without updating @PermissionsAllowed params; compile without -parameters losing formal names; reusing a Permission class across methods whose signatures differ.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/adbe4494a9e5b7b9. Report an issue: GitHub.