In a hook_menu() implementation, I am defining a route for login.

function my_account_menu() {
  $items['login'] = array(
    'title' => 'Customer Login',
    'page callback'    => 'drupal_get_form',
    'page arguments'   => array('my_account_login_form'),
    'access callback' => 'user_is_anonymous',
    'type' => MENU_CALLBACK

  return $items;

However, if I try accessing login/asdfdsa, it still loads the login page, instead of showing a 404 error.

Is there an error on the code I wrote, or in the module?

  • 2
    Believe it or not, that’s the expected behaviour. To the best of my knowledge it’s never been addressed. It’s been raised as an issue plenty of times, but never solved as far as I know – Clive Oct 17 '19 at 17:59

It's the expected behavior, and it's described in the documentation for hook_menu(), even it could not be clear, at a quick read.

Callback Functions

The definition for each path may include a page callback function, which is invoked when the registered path is requested. If there is no other registered path that fits the requested path better, any further path components are passed to the callback function. For example, your module could register path 'abc/def':

function mymodule_menu() {
  $items['abc/def'] = array(
    'page callback' => 'mymodule_abc_view',
  return $items;

function mymodule_abc_view($ghi = 0, $jkl = '') {
  // ...

When path 'abc/def' is requested, no further path components are in the request, and no additional arguments are passed to the callback function (so $ghi and $jkl would take the default values as defined in the function signature). When 'abc/def/123/foo' is requested, $ghi will be '123' and $jkl will be 'foo'. Note that this automatic passing of optional path arguments applies only to page and theme callback functions.

This means that, for example with login/asdfdsa, Drupal will first check if there are modules that handle that path. If there aren't modules handling that path, Drupal will check if there are modules handling every path matching login/*; if there aren't modules for those paths, Drupal will check if there are modules that handle login. In your case, that is your module. (I left out how Drupal handles the path aliases, as in this case isn't relevant.)

That happens also for other paths, for example node/1. If you have a node whose ID is 1, /node/1 shows it. If you try to access a path like, for example, /node/1/sdfgrgjpqwd432az, you will still see that node. (sdfgrgjpqwd432az is just a random string, to be sure I am not using a path for which a module defines a page callback.)

| improve this answer | |
  • I definitely read the documentation a bit too quickly. Thank you for this thorough explanation. – David Oct 29 '19 at 15:15
  • I know people that have been using Drupal for years and didn't notice that. Most people would tell you that a path like abc/def/123/foo is handled by your module if it defines a route like abc/def/%/%, which isn't true. – kiamlaluno Oct 29 '19 at 15:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.