quarkusio/quarkus · error · RuntimeException

Prefix path cannot end with /

Error message

Prefix path cannot end with /

What it means

PathMatcher.addPrefixPath rejects prefix paths that end with a trailing slash. Prefix paths are matched by string-prefix comparison, so a trailing slash is invalid; the framework normalizes this itself and a path like "/foo/" would never match correctly.

Source

Thrown at independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/mapping/PathMatcher.java:94

         * <p>
         * The match is done on a prefix bases, so registering /foo will also match /bar. Exact
         * path matches are taken into account first.
         * <p>
         * If / is specified as the path then it will replace the default handler.
         *
         * @param path The path
         * @param handler The handler
         */
        void addPrefixPath(final String path, final T handler) {
            if (path.isEmpty()) {
                throw new IllegalArgumentException("Path not specified");
            }

            if (STRING_PATH_SEPARATOR.equals(path)) {
                this.defaultHandler = handler;
                return;
            } else if (path.endsWith(STRING_PATH_SEPARATOR)) {
                throw new RuntimeException("Prefix path cannot end with /");
            }

            pathsBuilder.put(path, handler);
        }

        private int[] buildLengths(SubstringMap<T> paths) {
            final Set<Integer> lengths = new TreeSet<>(new Comparator<>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return -o1.compareTo(o2);
                }
            });
            for (String p : paths.keys()) {
                lengths.add(p.length());
            }

            int[] lengthArray = new int[lengths.size()];
            int pos = 0;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Strip the trailing slash before registration: if (path.endsWith("/") && path.length() > 1) path = path.substring(0, path.length() - 1)
  2. Fix the route configuration or @Path annotation so it does not end with '/'
  3. Use the exact root "/" only if you intend to register the default handler

Example fix

// before
matcher.addPrefixPath("/api/", handler); // throws

// after
String prefix = "/api/".replaceAll("/$", "");
matcher.addPrefixPath(prefix, handler); // "/api"
Defensive patterns

Strategy: validation

Validate before calling

if (path != null && path.length() > 1 && path.endsWith("/")) {
    throw new IllegalArgumentException("Prefix path cannot end with /: " + path);
}

Try / catch

try {
    matcher.addPrefixPath(path, handler);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().contains("cannot end with /")) {
        matcher.addPrefixPath(stripTrailingSlash(path), handler);
    } else throw e;
}

Prevention

When it happens

Trigger: Calling PathMatcher.addPrefixPath("/foo/", handler) — any registered prefix path whose last character is '/' (other than the exact root "/", which is handled as the default handler).

Common situations: Constructing route paths by concatenation ("/api/" + name) leaving trailing slashes; user-supplied configuration of route prefixes with trailing slash; converting full-path routes to prefix routes without stripping the trailing slash.

Related errors


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