Handling Errors Gracefully in Craft CMS
Tags: errors, frontend, craftcms
Published: 2020-02-19 22:11:30

---
title: Handling Errors Gracefully in Craft CMS
published: true
date: 2017-10-12 22:10:00 UTC
tags: errors,frontend,craftcms
canonical_url: https://nystudio107.com/blog/handling-errors-gracefully-in-craft-cms
---

# Handling Errors Gracefully in Craft CMS

### Every web­site can have errors, it’s how you han­dle them that mat­ters. Here’s a prac­ti­cal error han­dling guide for Craft CMS.

Andrew Welch / [nystudio107](https://nystudio107.com)

![Oh No Craft Error Monkey](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/oh-no-craft-error-monkey.jpg)

**Update:** This arti­cle has been updat­ed to use Craft CMS 3.x syntax.

We’ve all like­ly heard the epigram:

<aside>
                                Hap­pi­ness isn’t the lack of prob­lems, it’s know­ing how to deal with them.
                            </aside>

The same applies to web devel­op­ment. Your web­site will inevitably encounter error states you did­n’t expect, han­dling them grace­ful­ly is what matters.

This sim­ply requires adopt­ing some [Defen­sive Pro­gram­ming](https://scottdorman.blog/2008/07/04/what-is-defensive-programming/) tech­niques. While it’s a small amount of addi­tion­al work to code defen­sive­ly, once you start doing it, it’ll just become sec­ond nature.

And believe me, that extra up-front time will pay off in spades in terms of time spent debug­ging or patch­ing your work when sh\*t hits the fan.

## What it means to code defensively

The first thing you need to do is accept the pes­simistic atti­tude espoused by Murh­py’s law:

<aside>
                                Any­thing that can go wrong will go wrong
                            </aside>

While this may be an over­ly para­noid way to live life, it’s a per­fect­ly rea­son­able — even manda­to­ry — way to think about things when programming.

![Murphys Law Errors Happen](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/murphys-law-errors-happen.jpg)

What this means from a prac­ti­cal point of view is:

- Write your code in a mod­u­lar, reusable way
- If any method you call returns an error code, check it
- Nev­er assume that a method returns actu­al data; check for <kbd>null</kbd>
- Think of every con­ceiv­able way your client could mess things up
- Han­dle these error states grace­ful­ly, in a user-friend­ly way

While this may seem basic, I see a sur­pris­ing amount of code that blithe­ly assumes every­thing _just works_. Take the oppo­site approach; assume failure.

What hap­pens then is when an error state does hap­pen, the web­site falls over, and you get pan­icked calls from the client in the wee hours.

## Defen­sive Cod­ing in Twig

This men­tal­i­ty of defen­sive cod­ing applies no mat­ter what lan­guage you’re using. Whether it’s C, PHP, JavaScript, or even tem­plat­ing lan­guages like [Twig](https://twig.symfony.com/doc/2.x/).

A good way to write code is to adopt cod­ing stan­dards; this ensures that you’re always doing things in a con­sis­tent way. This becomes more impor­tant when you’re work­ing with a team, but future-you will also ben­e­fit from con­sis­tent, stan­dard­ized coding.

You can adopt the Sen­sio Labs [Twig Cod­ing Stan­dards](https://twig.symfony.com/doc/2.x/coding_standards.html), or you can adopt your own.

<aside>
                                It’s less impor­tant what cod­ing stan­dards you adopt than it is that you’re using some type of standard.
                            </aside>

Now that you’re on-board adopt­ing some form of cod­ing stan­dards, let’s talk about things you can do while you’re devel­op­ing a Craft CMS website.

The very first thing you absolute­ly **must** be doing is have [dev­Mode](https://craftcms.com/support/dev-mode) <kbd>ON</kbd> for local devel­op­ment. Hav­ing <kbd>devMode</kbd> on will catch all sorts of soft errors that might not be appar­ent if you don’t have it on.

The [Mul­ti-Envi­ron­ment Con­fig for Craft CMS](https://dev.to/gaijinity/multi-environment-config-for-craft-cms-3952-temp-slug-5514305) arti­cle shows how you can have <kbd>devMode</kbd> (amongst oth­er things) on or off depend­ing on the environment.

With <kbd>devMode</kbd> on, and all of our soft errors fixed, let’s have a look at cod­ing defen­sive­ly in Twig. A very com­mon pat­tern when using Twig with Craft is doing some­thing like this:

```

{% set image = entry.someImage.one() %}
<img src="{{ image.getUrl() }}" />

```

What could pos­si­bly go wrong? Well, a lot!

- What if the <kbd>entry</kbd> does­n’t exist?
- What if the client nev­er added an Asset to the <kbd>someImage</kbd> field?
- What if we did­n’t restrict the client to upload­ing only images, and they added an Adobe Illus­tra­tor file?

These are the types of things you should be think­ing about when you’re in mid-keystroke.

## Coa­lesc­ing the Night Away

What I’ve found to be the most effec­tive for han­dling this type of com­mon pat­tern is to use the Twig <kbd>??</kbd> null coa­lesc­ing oper­a­tor. It looks some­thing like this:

```

{% set image = entry.someImage.one() ?? someGlobal.defaultImage.one() ?? null %}
<img src="{{ image.getUrl() }}" />

```

What this does is set the image to the first expres­sion that is defined and not <kbd>null</kbd>. So it’ll use <kbd>entry.someImage.one()</kbd> if that is defined/​not <kbd>null</kbd>, or <kbd>someGlobal.defaultImage</kbd> if that is defined/​not <kbd>null</kbd>, and final­ly just return <kbd>null</kbd> if noth­ing else matched.

It han­dles check­ing each object in the ​“dot nota­tion” syn­tax, so for exam­ple it will make sure that <kbd>entry</kbd>, <kbd>someImage</kbd>, and the result of <kbd>one()</kbd> are all defined and not null.

In this case, <kbd>someGlobal</kbd> is a [Craft CMS Glob­al](https://craftcms.com/docs/globals), in which we can put a default image if one has­n’t been filled in. While this isn’t required, it can be nice in some cir­cum­stances, and shows how the <kbd>??</kbd> null coa­lesc­ing oper­a­tor can be passed any num­ber of fallbacks.

This is much nicer than doing some­thing like:

```

{% if entry is defined and entry |length and entry.someImage is defined and entry.someImage | length %}

```

The null coa­lesc­ing oper­a­tor is built into Twig as of Twig ver­sion 1.24 (Jan­u­ary 25<sup>th</sup>, 2016), which is avail­able in Craft as of [Craft 2.6.771](https://craftcms.com/changelog#2-6-2771) (March 8<sup>th</sup>, 2016).

If you want to know more about the null coa­lesc­ing oper­a­tor, check out the [Twig’s null-coa­lesc­ing oper­a­tor (??)!](https://straightupcraft.com/events/twigs-null-coalescing-operator) Straight Up Craft Hangout.

Obser­vant read­ers will note that we could still end up with a null val­ue for image here; so let’s address that too:

```

{% set image = entry.someImage.one() ?? someGlobal.defaultImage.one() ?? null %}
{% if image and image.kind == "image" %}
    <img src="{{ image.getUrl() }}" />
{% endif %}

```

That looks a lot bet­ter. We’re mak­ing sure that the <kbd>image</kbd> is not <kbd>null</kbd>, and we make sure that the <kbd>image.kind</kbd> an actu­al [image file type](https://craftcms.com/docs/templating/assetfilemodel#kind). How­ev­er, we should also remem­ber to restrict the type of Assets that the client can upload to be just images as well.

I’ll even go one step fur­ther, and state that it’s a mis­take to _ever_ be out­putting any image that the client has uploaded. As stat­ed in the [Cre­at­ing Opti­mized Images in Craft CMS](https://dev.to/gaijinity/creating-optimized-images-in-craft-cms-2hc5-temp-slug-2354251) arti­cle, we should be trans­form­ing and opti­miz­ing any images dis­played on the frontend.

The null coa­lesc­ing oper­a­tor is a nice way to do all of the req­ui­site defined/​not <kbd>null</kbd> checks, and pro­vide fall­backs and defaults. How­ev­er, because a vari­able set to an emp­ty string is defined and not <kbd>null</kbd>, this might not do what you’d expect:

```

{% set thisTitle = entry.title ?? category.title ?? global.title ?? 'Some Default Title' %}

```

The prob­lem here is it’ll _just_ pick the first thing that is defined and not <kbd>null</kbd>. So if <kbd>entry.title</kbd> is an emp­ty string, it’ll use that, which is rarely what you want.

This is why I wrote the free [Emp­ty Coa­lesce](http://github.com/nystudio107/craft-emptycoalesce) plu­g­in for Craft CMS 3. Emp­ty Coa­lesce adds the <kbd>???</kbd> oper­a­tor to Twig that will return the first thing that is defined, not null, _and_ not emp­ty. It’s that last bit that is key! So it becomes:

```

{% set thisTitle = entry.title ??? category.title ??? global.title ??? 'Some Default Title' %}

```

Now the first thing that is defined, not null, _and_ not emp­ty will be what <kbd>thisTitle</kbd> is set to.

Nice. Sim­ple. Read­able. And most impor­tant­ly, like­ly the result you’re expecting.

## Han­dling Excep­tions on the Frontend

So what about oth­er errors that can hap­pen on the fron­tend, like web serv­er or data­base errors? In Craft, these are known as _excep­tions_. They aren’t sup­posed to hap­pen, so they are _excep­tion­al_.

![Server Fire Handling Exceptions](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/server-fire-handling-exceptions.jpg)

The most well-known excep­tion is a <kbd>404</kbd> error, which hap­pens when there is a request for a file that does­n’t exist on the web serv­er. In these cas­es, we want to han­dle it grace­ful­ly, and dis­play a nice friend­ly error.

This is good for SEO rea­sons, and it’s also just plain friend­ly to the peo­ple who are vis­it­ing your web­site. If there is a 404 error on the nys​tu​dio107​.com web­site, for instance, we encour­age peo­ple to stick around:

![Nystudio107 404 Error](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_992x813_crop_center-center_100_line/nystudio107-404-error.png)

The way this works in Craft is that is looks for a tem­plate named after the http sta­tus code, in this case the name would be <kbd>404.html</kbd> or <kbd>404.twig</kbd>.

By default it looks for these in the root of your tem­plates fold­er, but you can put them any­where you want using the [errorTem­platePre­fix](http://docs.craftcms.com/v2/config-settings.html#templating) con­fig setting.

The inter­est­ing thing here is that this works for _any_ [http sta­tus code](https://httpstatuses.com/). Just name the tem­plate after the http sta­tus code, and away you go.

Why might you want to do this? Well, the default Craft error han­dler will dis­play an error code and an error mes­sage. If the worst hap­pens, you might want to put a friend­lier face on it for your clients than just some­thing like:

![Craft Cdbexception Error](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x118_crop_center-center_100_line/craft-cdbexception-error.png)

Addi­tion­al­ly, some pen­e­tra­tion tests will flag this as a secu­ri­ty issue, because it’s giv­ing infor­ma­tion about the type of data­base used, the nature of the error, etc.

It’d be much bet­ter for every­one con­cerned if we just had a nice friend­ly error mes­sage, such as the one Twit­ter was famous for:

![Twitter Fail Whale](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_768x530_crop_center-center_100_line/twitter-fail-whale.png)

We might not want to cre­ate a sep­a­rate tem­plate for every pos­si­ble http sta­tus code, though, so let’s dive a bit deep­er. This is exact­ly what Craft does when it looks for a tem­plate to ren­der when an error happens:

```

/**
	 * Renders an error template.
	 *
	 * @throws \Exception
	 * @return null
	 */
	public function actionRenderError()
	{
		$error = craft()->errorHandler->getError();
		$code = (string) $error['code'];

		if (craft()->request->isSiteRequest())
		{
			$prefix = craft()->config->get('errorTemplatePrefix');

			if (craft()->templates->doesTemplateExist($prefix.$code))
			{
				$template = $prefix.$code;
			}
			else if ($code == 503 && craft()->templates->doesTemplateExist($prefix.'offline'))
			{
				$template = $prefix.'offline';
			}
			else if (craft()->templates->doesTemplateExist($prefix.'error'))
			{
				$template = $prefix.'error';
			}
		}

```

So it first looks for a tem­plate that match­es the exact http sta­tus code. If that’s not found, then if it’s a <kbd>503</kbd> ser­vice not avail­able error, it looks for a tem­plate named <kbd>offline</kbd> (more on that lat­er), and final­ly it falls back on just look­ing for a tem­plate named <kbd>error</kbd>.

So per­fect! We can cre­ate error han­dling tem­plates for spe­cif­ic error codes that we want to han­dle in a spe­cial way, like <kbd>404</kbd> errors, and then we can have a gener­ic catch-all <kbd>error.html</kbd> or <kbd>error.twig</kbd> template!

Now we have an easy way to grace­ful­ly han­dle any errors that hap­pen in a user-friend­ly way, and we can con­trol what error infor­ma­tion is ever dis­played pub­licly. It’s not as cool as the Fail Whale, but it’s bet­ter than a scary <kbd>CDbException</kbd> error:

![Nystudio107 Generic Error](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_992x813_crop_center-center_100_line/nystudio107-generic-error.png)

**N.B.:** If you have [dev­Mode](https://craftcms.com/support/dev-mode) on (which you should always have on when devel­op­ing in <kbd>local</kbd> dev), all Craft excep­tions will be dis­played using the Craft debug tem­plate. That means they will _not_ be rout­ed to your cus­tom error pages. To test them, either turn <kbd>devMode</kbd> off, or just nav­i­gate direct­ly to the tem­plate in question.

## Offline Stag­ing Sites

Ear­li­er we dis­cussed that a <kbd>503</kbd> ser­vice unavail­able error will result in Craft spe­cial-cas­ing for an <kbd>offline.html</kbd> or <kbd>offline.twig</kbd> template.

We can take advan­tage of this behav­ior to make sure our client stag­ing sites are not only not being index by Google­Bot and oth­er such crawlers, but also that only our client can see the web­site as we work on it.

In the [Pre­vent­ing Google from Index­ing Stag­ing Sites](https://dev.to/gaijinity/preventing-google-from-indexing-staging-sites-cbc-temp-slug-7434196) arti­cle, we dis­cussed using robots.txt and a mul­ti-envi­ron­ment set­up to do this. Here’s anoth­er way to do it that you may find even more convenient.

![Craft Cms Is System On](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_576x192_crop_center-center_100_line/craft-cms-is-system-on.png)

Craft has a con­fig set­ting called [isSys­tem­Live](http://craftcms.com/docs/config-settings#isSystemOn) (for­mer­ly <kbd>isSystemOn</kbd>) that we can set to <kbd>false</kbd> for stag­ing or oth­er envi­ron­ments that we don’t want any­one to access. This is a mul­ti-envi­ron­ment con­fig vari­able, that we might set some­thing like this:

```

<?php

/**
 * General Configuration
 *
 * All of your system's general configuration settings go in here.
 * You can see a list of the default settings in craft/app/etc/config/defaults/general.php
 */

// $_ENV constants are loaded from .env.php via public/index.php
return array(

    // All environments
    '*' => array(
        'omitScriptNameInUrls' => true,
        'usePathInfo' => true,
        'cacheDuration' => false,
        'cacheMethod' => 'redis',
        'useEmailAsUsername' => true,
        'generateTransformsBeforePageLoad' => true,
        'requireMatchingUserAgentForSession' => false,
        'userSessionDuration' => 'P1W',
        'rememberedUserSessionDuration' => 'P4W',
        'siteUrl' => getenv('CRAFTENV_SITE_URL'),
        'craftEnv' => CRAFT_ENVIRONMENT,
        'backupDbOnUpdate' => false,
        'defaultSearchTermOptions' => array(
            'subLeft' => true,
            'subRight' => true,
        ),

        'defaultTemplateExtensions' => array('html', 'twig', 'rss'),

        // Set the environmental variables
        'environmentVariables' => array(
            'baseUrl' => getenv('CRAFTENV_BASE_URL'),
            'basePath' => getenv('CRAFTENV_BASE_PATH'),
            'staticAssetsVersion' => '106',
        ),
    ),

    // Live (production) environment
    'live' => array(
        'isSystemLive' => true,
        'devMode' => false,
        'enableTemplateCaching' => true,
        'allowAutoUpdates' => false,
    ),

    // Staging (pre-production) environment
    'staging' => array(
        'isSystemLive' => false,
        'devMode' => false,
        'enableTemplateCaching' => true,
        'allowAutoUpdates' => false,
    ),

    // Local (development) environment
    'local' => array(
        'isSystemLive' => true,
        'devMode' => true,
        'enableTemplateCaching' => false,
        'allowAutoUpdates' => true,
        'disableDevmodeMinifying' => true,

        // Set the environmental variables
        'environmentVariables' => array(
            'baseUrl' => getenv('CRAFTENV_BASE_URL'),
            'basePath' => getenv('CRAFTENV_BASE_PATH'),
            'staticAssetsVersion' => time(),
        ),
    ),
);

```

What this set­ting does is it caus­es Craft to return a <kbd>503</kbd> ser­vice unavail­able error code for any fron­tend request. This then caus­es Craft to throw an excep­tion, and dis­play the <kbd>offline</kbd> tem­plate. This is great, because we can con­trol what appears there.

So this stops Google­Bot, crawlers, and oth­er pry­ing eyes from see­ing the web­site as we work on it, but what about our clients?

![Craft Offline Access Permissions](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_293x225_crop_center-center_line/craft-offline-access-permissions.png)

In the AdminCP, **Set­tings** → **Users** → **User**  **Group** lets you set access per­mis­sions for user groups. As long as your client has **Access the site when the sys­tem is off** per­mis­sion, they can log in to the AdminCP, and then access the site with aplomb.

Here’s what our <kbd>503</kbd> tem­plate looks like (it can be either <kbd>503</kbd> or <kbd>offline</kbd>, either works):

![Nystudio107 503 Error](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_992x677_crop_center-center_100_line/nystudio107-503-error.png)

You could even have your offline page be a [fron­tend login form](https://craftcms.com/docs/templating/login-form) for your clients, to make it even eas­i­er for them.

In your code, you can even do con­di­tion­als based on <kbd>isSystemLive</kbd> too:

```

{% if craft.config.isSystemLive %}
    Hey, we're online!
{% else %}
    Ut oh, we're offline…
{% endif %}

```

## Wrap­ping Up

Since a theme of the arti­cle has been to use some well-known pearls of wis­dom, I leave you with one more:

<aside>
                                An ounce of pre­ven­tion is worth a pound of cure
                            </aside>

The time you spend cod­ing defen­sive­ly and bul­let­proof­ing your web­sites will pay off with hap­py clients, and less frus­tra­tion for you as a developer.

![Happy Baby Face](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/happy-baby-face.jpg)

At the very least, future-you will be extreme­ly grate­ful that past-you did such a good job… because it’s almost inevitable that you’ll be the one work­ing on it in the future.

## Further Reading

_If you want to be notified about new articles, follow [nystudio107](https://twitter.com/nystudio107) on Twitter._

<small>Copyright ©2020 nystudio107. Designed by nystudio107</small>