Hello, I'm currently working on creating custom error pages on Drupal 9.

I know I can easily add customized pages for 404 and 403, but how can I use custom pages for all possible errors like 401, 403, 500, 502, and etc.?

I found this module: https://www.drupal.org/project/error_page but I couldn't see the result, not so sure which part I messed up with. And some say I could add custom hooks - but I'm not familiar with php and twig, so it was very hard for me to follow with no detailed guidance.

Can someone help me with either using that module or teach me if there's any easier way to configure those?

Thank you.

Comments

dariemlazaro’s picture

You can try this and modify to add 5xx suggestions.
And then you can use page--401.html.twig page--403.html.twig and page--404.html.twig
/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function theme-name_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  
  /**
   * 4xx template suggestion.
   */
  if (!is_null(Drupal::requestStack()->getCurrentRequest()->attributes->get('exception'))) {
    $status_code = Drupal::requestStack()->getCurrentRequest()->attributes->get('exception')->getStatusCode();
    switch ($status_code) {
      case 401:
        // Unauthorized Access.
        $error = 401;
        break;

      case 403:
        // Access Denied.
        $error = 403;
        break;

      case 404:
        // Page Not Found.
        $error = 404;
        break;
      default:
        break;
    }
  }
  if (isset($error)) {
    $suggestions[] = 'page__' . $error;
  }
}
sops’s picture

@dariemlazaro Thank you so much! Where exactly I'd need to add these files? And does this mean, i could create page--500.html.twig and etc?

mmjvb’s picture

it needs a change for the error 500.

$error = Drupal::requestStack()
         ->getCurrentRequest()
         ->attributes->get('exception')
         ->getStatusCode();

if (!is_null($error)) {
    switch ($error) {
      case 401:
      case 403:
      case 404:
      case 500:
         $suggestions[] = 'page__' . $error;
  }
}

Optimized the code in the function making it less verbose. Apart from the 500 it is equivalent to the proposed code. Assume it works, didn't look at that.

sops’s picture

Thank you. So, I assume that from here:

$suggestions[] = 'page__' . $error;

I can create a page name starting with page__500.html.twig something like that for customization?

But I wondered where should I add this function? Like under which folder and what page?

mmjvb’s picture

You add the function to your theme !

sops’s picture

Thank you! So, I'm using custom theme - have four folders including templates folder: this folder has just one file page.html.twig

and I have two files info.yml and libraries.yml, where should I add this function?

Should I add it to page.html.twig? and also create new files for page__(error number).html twig for each error like 401, 403, 503, and etc and add that function?

mmjvb’s picture

Please read this topic again and process the information provided. You have been told where to add this now 4 times!

docker@d946:/var/www/web/core/themes$ ls olivero
config       fonts   logo.svg                 olivero.libraries.yml  src
css          images  olivero.breakpoints.yml  olivero.theme          templates
favicon.ico  js      olivero.info.yml         screenshot.png         theme-settings.php
docker@d946:/var/www/web/core/themes$

For the core theme olivero, it would be olivero.theme.

sops’s picture

Thank you! Sorry, I'm pretty new to drupal -- so everything seems pretty confusing to me. And I'm not using olivero theme, i'm using my own custom theme, so I was confused. As, I don't have that {themename}.theme file :/ so it seems like I'll have to create a file first.

But how about creating a custom error html page? Can I create page__{errorname}.html.twig in my custom theme folder?

And thank you so much for your help.

mmjvb’s picture

Having a custom theme suggests familiarity with themes. Indeed, when that file doesn't exist you need to add yourself. 

Twig templates go in .... indeed in templates. Suggest to look at existing themes.

dariemlazaro’s picture

Oh thanks, I didn't know you could do that in a switch() and yes now it is much cleaner and legible.

dariemlazaro’s picture

I get this error with your code

The website encountered an unexpected error. Please try again later.
Error: Call to a member function getStatusCode() on null in theme-name_theme_suggestions_page_alter() (line 213 of themes\custom\theme-name\theme-name.theme).

theme-name_theme_suggestions_page_alter(Array, Array, 'page') (Line: 449)
Drupal\Core\Theme\ThemeManager->alterForTheme(Object, 'theme_suggestions', Array, Array, 'page') (Line: 458)
Drupal\Core\Theme\ThemeManager->alter(Array, Array, Array, 'page') (Line: 245)
Drupal\Core\Theme\ThemeManager->render('page', Array) (Line: 422)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 201)
Drupal\Core\Render\Renderer->render(Array) (Line: 479)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 88)
__TwigTemplate_95f67010980c89e2451768d4c345857c3a1017f1c11b476f99340255912ead7e->doDisplay(Array, Array) (Line: 405)
Twig\Template->displayWithErrorHandling(Array, Array) (Line: 378)
Twig\Template->display(Array) (Line: 390)
Twig\Template->render(Array) (Line: 55)
twig_render_template('themes/custom/theme-name/templates/layout/html.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('html', Array) (Line: 422)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 201)
Drupal\Core\Render\Renderer->render(Array) (Line: 162)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 564)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 163)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object)
call_user_func(Array, Object, 'kernel.view', Object) (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch(Object, 'kernel.view') (Line: 164)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 709)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

the full code working now

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function theme_name_theme_suggestions_page_alter(array &$suggestions, array $variables) {
  if (!is_null(Drupal::requestStack()->getCurrentRequest()->attributes->get('exception'))) {
    $error = Drupal::requestStack()->getCurrentRequest()->attributes->get('exception')->getStatusCode();

  if (!is_null($error)) {
    switch ($error) {
      case 401:
      case 403:
      case 404:
      case 500:
      case 503:
      case 504:
        $suggestions[] = 'page__' . $error;
    }
  }
  }
}

and if you need more error pages just add a case to the switch with the error code before $suggestions[] = 'page__' . $error;

mmjvb’s picture

Assume it works, didn't look at that.

Common mistake in Drupal. Assuming things rather that checking. Agree with the solution, still propose something different.

$exception = Drupal::requestStack()->getCurrentRequest()->attributes->get('exception');
if ($exception) {
   $error = $exception->getStatusCode();
    switch 
...
...
...
}

This is due to the DRY principle. In this case use $exception as a pointer to exception. So it doesn't need to be executed twice.