diff --git a/include/ajax.content.php b/include/ajax.content.php
index 25c66e3c389ebd12ff1eae3a62f3eba44402ae4f..cfdd7b99f0c02085793e8b1194ed46d1ce4ad911 100644
--- a/include/ajax.content.php
+++ b/include/ajax.content.php
@@ -13,9 +13,10 @@
 
     vim: expandtab sw=4 ts=4 sts=4:
 **********************************************************************/
-
 if(!defined('INCLUDE_DIR')) die('!');
 
+require_once INCLUDE_DIR.'class.ajax.php';
+
 class ContentAjaxAPI extends AjaxController {
 
     function log($id) {
@@ -208,51 +209,10 @@ class ContentAjaxAPI extends AjaxController {
         if (!$_GET['root'])
             Http::response(400, '`root` is required parameter');
 
-        switch ($_GET['root']) {
-        case 'cannedresponse':
-            $roots = array('ticket');
-            break;
-
-        default:
-            if ($info = Page::getContext($_GET['root'])) {
-                $roots = $info;
-                break;
-            }
-
-            // Get the context for an email template
-            $tpl_info = EmailTemplateGroup::getTemplateDescription($_GET['root']);
-            if (!$tpl_info)
-                Http::response(422, 'No such context');
-            $roots = $tpl_info['context'];
-        }
+        $items = VariableReplacer::getContextForRoot($_GET['root']);
 
-        $contextTypes = array(
-            'activity' => __('Type of recent activity'),
-            'assignee' => array('class' => 'Staff', 'desc' => __('Assigned agent/team')),
-            'assigner' => array('class' => 'Staff', 'desc' => __('Agent performing the assignment')),
-            'comments' => __('Assign/transfer comments'),
-            'link' => __('Access link'),
-            'message' => array('class' => 'MessageThreadEntry', 'desc' => 'Message from the EndUser'),
-            'note' => array('class' => 'NoteThreadEntry', 'desc' => __('Internal note')),
-            'poster' => array('class' => 'User', 'desc' => 'EndUser or Agent originating the message'),
-            // XXX: This could be EndUser -or- Staff object
-            'recipient' => array('class' => 'TicketUser', 'desc' => 'Message recipient'),
-            'response' => array('class' => 'ResponseThreadEntry', 'desc' => __('Outgoing response')),
-            'signature' => 'Selected staff or department signature',
-            'staff' => array('class' => 'Staff', 'desc' => 'Agent originating the activity'),
-            'ticket' => array('class' => 'Ticket', 'desc' => 'The ticket'),
-            'user' => array('class' => 'User', 'desc' => __('Message recipient')),
-        );
-        $context = array();
-        foreach ($roots as $C=>$desc) {
-            // $desc may be either the root or the description array
-            if (is_array($desc))
-                $context[$C] = $desc;
-            else
-                $context[$desc] = $contextTypes[$desc];
-        }
-        $global = osTicket::getVarScope();
-        $items = VariableReplacer::compileScope($context + $global);
+        if (!$items)
+            Http::response(422, 'No such context');
 
         header('Content-Type: application/json');
         return $this->encode($items);
diff --git a/include/class.template.php b/include/class.template.php
index c656e236ab6bf7d6b0b943908bd7ca13d9bff5dc..a48e5255d47dac70bea9234388b6ef81f35b6bf3 100644
--- a/include/class.template.php
+++ b/include/class.template.php
@@ -508,6 +508,22 @@ class EmailTemplate {
         return $this->getGroup()->getTemplateDescription($this->ht['code_name']);
     }
 
+    function getInvalidVariableUsage() {
+        $context = VariableReplacer::getContextForRoot($this->ht['code_name']);
+        $invalid = array();
+        foreach (array($this->getSubject(), $this->getBody()) as $B) {
+            $variables = array();
+            if (!preg_match_all('`%\{([^}]*)\}`', $B, $variables, PREG_SET_ORDER))
+                continue;
+            foreach ($variables as $V) {
+                if (!isset($context[$V[1]])) {
+                    $invalid[] = $V[0];
+                }
+            }
+        }
+        return $invalid;
+    }
+
     function update($vars, &$errors) {
 
         if(!$this->save($this->getId(),$vars,$errors))
diff --git a/include/class.variable.php b/include/class.variable.php
index 55fac7a2c41a35a17608a9466d72626fa165fca0..e026800864645d5de2f9a688601650d5565b7bd8 100644
--- a/include/class.variable.php
+++ b/include/class.variable.php
@@ -210,6 +210,55 @@ class VariableReplacer {
         }
         return $items;
     }
+
+    static function getContextForRoot($root) {
+        switch ($root) {
+        case 'cannedresponse':
+            $roots = array('ticket');
+            break;
+
+        default:
+            if ($info = Page::getContext($root)) {
+                $roots = $info;
+                break;
+            }
+
+            // Get the context for an email template
+            if ($tpl_info = EmailTemplateGroup::getTemplateDescription($root))
+                $roots = $tpl_info['context'];
+        }
+
+        if (!$roots)
+            return false;
+
+        $contextTypes = array(
+            'activity' => __('Type of recent activity'),
+            'assignee' => array('class' => 'Staff', 'desc' => __('Assigned agent/team')),
+            'assigner' => array('class' => 'Staff', 'desc' => __('Agent performing the assignment')),
+            'comments' => __('Assign/transfer comments'),
+            'link' => __('Access link'),
+            'message' => array('class' => 'MessageThreadEntry', 'desc' => 'Message from the EndUser'),
+            'note' => array('class' => 'NoteThreadEntry', 'desc' => __('Internal note')),
+            'poster' => array('class' => 'User', 'desc' => 'EndUser or Agent originating the message'),
+            // XXX: This could be EndUser -or- Staff object
+            'recipient' => array('class' => 'TicketUser', 'desc' => 'Message recipient'),
+            'response' => array('class' => 'ResponseThreadEntry', 'desc' => __('Outgoing response')),
+            'signature' => 'Selected staff or department signature',
+            'staff' => array('class' => 'Staff', 'desc' => 'Agent originating the activity'),
+            'ticket' => array('class' => 'Ticket', 'desc' => 'The ticket'),
+            'user' => array('class' => 'User', 'desc' => __('Message recipient')),
+        );
+        $context = array();
+        foreach ($roots as $C=>$desc) {
+            // $desc may be either the root or the description array
+            if (is_array($desc))
+                $context[$C] = $desc;
+            else
+                $context[$desc] = $contextTypes[$desc];
+        }
+        $global = osTicket::getVarScope();
+        return self::compileScope($context + $global);
+    }
 }
 
 class PlaceholderList
diff --git a/include/staff/tpl.inc.php b/include/staff/tpl.inc.php
index cfbbe5790896adcd70b350dede508b42c96dde1d..70c9e90b44f6ae456df72c9816992d9a6e881624 100644
--- a/include/staff/tpl.inc.php
+++ b/include/staff/tpl.inc.php
@@ -99,6 +99,19 @@ $tpl=$msgtemplates[$selected];
 <?php } ?>
 </div>
 
+<?php
+$invalid = array();
+if ($template instanceof EmailTemplate) {
+    if ($invalid = $template->getInvalidVariableUsage()) {
+    $invalid = array_unique($invalid); ?>
+    <div class="warning-banner"><?php echo
+        __('Some variables may not be a valid for this context. Please check for spelling errors and correct usage for this template.') ?>
+    <br/>
+    <code><?php echo implode(', ', $invalid); ?></code>
+</div>
+<?php }
+} ?>
+
 <div style="padding-bottom:3px;" class="faded"><strong><?php echo __('Email Subject and Body'); ?>:</strong></div>
 <div id="toolbar"></div>
 <div id="save" style="padding-top:5px;">