Different 404 behaviour on custom URL For a client's website I have these URLs:

library
library/{category}


I have a ParamConverter to verify that {category} is correct and return the taxonomy term. And each URL requires a "view library" permission which anonymous doesn't have.

The client's requirements are that 404 errors redirect the user to the  login page if not logged in.

I created a RedirectOn404Subscriber event subscriber to handle the 404 and redirect as needed. (I have tested it with a logged-in user and can redirect using this.)

When not logged in the redirection works fine for any obvious URL error:

example.com/xxxxx


And works for these:

example.com/library
example.com/library/valid-category


But this does not redirect:

example.com/library/invalid-category


It generates the 404 error but does not hit the redirect code.

It's as if it's not executing the ParamConverter when it's wrong - but it can't know that until it's executed the code...

Clearing caches just prior to a test does not alter the behaviour.

UPDATED TO INCLUDE CODE

404 redirect...

  /**
   * Prevents this being called when it's not an HTML request.
   *
   * @return array
   */
  protected function getHandledFormats() {
    return ['html'];
  }

  /**
   * Specifies the priority of all listeners in this class.
   *
   * The default priority is 1, which is very low. To have listeners that have
   * a "first attempt" at handling exceptions return a higher priority.
   *
   * @return int
   *   The event priority of this subscriber.
   */
  protected static function getPriority() {
    return 750;
  }

  /**
   *
   *
   * @param GetResponseForExceptionEvent $event
   */
  public function on404(GetResponseForExceptionEvent $event) {
    if ($this-&gt;currentUser-&gt;isAnonymous()) {
      // Any 404 when not logged in, return to front page.
      $route_name = '&lt;front&gt;';
    }
    else {
      // Any 404 when logged in, return to base page for user.
      #$route_name = 'user.page';
      return;
    }

    $query = $event-&gt;getRequest()-&gt;query-&gt;all();
    $redirect_uri = Url::fromRoute($route_name, [], ['query' =&gt; $query]);

    $returnResponse = new RedirectResponse($redirect_uri-&gt;toString());
    $event-&gt;setResponse($returnResponse);
  }


403 redirect...

  public function on403(GetResponseForExceptionEvent $event) {
    $request = $event-&gt;getRequest();
    $is_anonymous = $this-&gt;currentUser-&gt;isAnonymous();
    $route_name = $request-&gt;attributes-&gt;get('_route');
    $is_not_login = $route_name != 'user.login';
    if ($is_anonymous) {
      if ($is_not_login) {
        $query = $request-&gt;query-&gt;all();
        $redirect_uri = Url::fromRoute('&lt;front&gt;', [], ['query' =&gt; $query])-&gt;toString();
      }
      else {
        // Do not redirect, this is an anonymous login.
        $redirect_uri = NULL;
      }
    }
    else {
      // Any 403 when logged in, return to base page for user.
      #$redirect_uri = Url::fromRoute('user.page')-&gt;toString();
      return;
    }

    if (!empty($redirect_uri)) {
      $returnResponse = new RedirectResponse($redirect_uri);
      $event-&gt;setResponse($returnResponse);
    }
  }
Pasting the code made me think about the problem in more detail.

It's a question of which gets sent first: 403 (Access Denied) or 404 (Page Not Found).

If it's a legal URL ("library" or "library/valid-category") but the user doesn't have access, they get a 403 and redirected.

If it's not a legal URL ("library/invalid-category") it gets reported as a 404 ... even though they don't have access anyway.

Makes sense.