diff --git a/includes/menu.inc b/includes/menu.inc
index fa5a71e..1213e8f 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -395,10 +395,18 @@ function menu_get_ancestors($parts) {
  * @return
  *   The unserialized $data array, with path arguments replaced.
  */
-function menu_unserialize($data, $map) {
+function menu_unserialize($data, $map, $item) {
   if ($data = unserialize($data)) {
     foreach ($data as $k => $v) {
       if (is_int($v)) {
+        // Load the object for the given id.
+        if (!empty($item['load_functions']) && !_menu_load_objects($item, $map)) {
+          // An error occurred loading an object.
+          // Do not set access to FALSE: results in unwanted 404-error
+          // on non-existing page, like content/123.
+          // $item['access'] = FALSE; is done in _menu_load_objects().
+          return FALSE;
+        }
         $data[$k] = isset($map[$v]) ? $map[$v] : '';
       }
     }
@@ -477,8 +485,8 @@ function menu_get_item($path = NULL, $router_item = NULL) {
       }
       if ($router_item['access']) {
         $router_item['map'] = $map;
-        $router_item['page_arguments'] = array_merge(menu_unserialize($router_item['page_arguments'], $map), array_slice($map, $router_item['number_parts']));
-        $router_item['theme_arguments'] = array_merge(menu_unserialize($router_item['theme_arguments'], $map), array_slice($map, $router_item['number_parts']));
+        $router_item['page_arguments'] = array_merge(menu_unserialize($router_item['page_arguments'], $map, $router_item) ? menu_unserialize($router_item['page_arguments'], $map, $router_item) : array(), array_slice($map, $router_item['number_parts']));
+        $router_item['theme_arguments'] = array_merge(menu_unserialize($router_item['theme_arguments'], $map, $router_item) ? menu_unserialize($router_item['theme_arguments'], $map, $router_item) : array(), array_slice($map, $router_item['number_parts']));
       }
     }
     $router_items[$path] = $router_item;
@@ -557,6 +565,21 @@ function _menu_load_objects(&$item, &$map) {
     if (!is_array($load_functions)) {
       $load_functions = unserialize($load_functions);
     }
+
+    // If the object is loaded before, don't load it again,
+    // but do update &$map with _cached_object_map.
+    if (isset($item['_cached_object_map'])) {
+      $map = $item['_cached_object_map'];
+      if ($item['_cached_object_map'] === FALSE) {
+          $item['access'] = FALSE;
+          return FALSE;
+      }
+      else {
+        return TRUE;
+      }
+    }
+    $item['_cached_object_map'] = array();
+
     $path_map = $map;
     foreach ($load_functions as $index => $function) {
       if ($function) {
@@ -596,12 +619,14 @@ function _menu_load_objects(&$item, &$map) {
         if ($return === FALSE) {
           $item['access'] = FALSE;
           $map = FALSE;
+          $item['_cached_object_map'] = $map;
           return FALSE;
         }
         $map[$index] = $return;
       }
     }
     $item['load_functions'] = $load_functions;
+    $item['_cached_object_map'] = $map;
   }
   return TRUE;
 }
@@ -615,9 +640,17 @@ function _menu_load_objects(&$item, &$map) {
  *   An array of path arguments; for example, array('node', '5').
  *
  * @return
+ *   TRUE if the object-at-hand could be loaded, FALSE otherwise.
  *   $item['access'] becomes TRUE if the item is accessible, FALSE otherwise.
  */
 function _menu_check_access(&$item, $map) {
+  // menu_tree_check_access() may set this ahead of time for links to nodes.
+  if (isset($item['access'])) {
+    if ($item['access'] === FALSE) {
+      return FALSE;
+    }
+    return TRUE;
+  }
   $item['access'] = FALSE;
   // Determine access callback, which will decide whether or not the current
   // user has access to this path.
@@ -627,7 +660,13 @@ function _menu_check_access(&$item, $map) {
     $item['access'] = (bool) $callback;
   }
   else {
-    $arguments = menu_unserialize($item['access_arguments'], $map);
+    $arguments = menu_unserialize($item['access_arguments'], $map, $item);
+    if ($arguments === FALSE) {
+      // An error happened when loading the object. $item['access'] is now set to FALSE.
+      $item['access'] = FALSE;
+      return FALSE;
+    }
+
     // As call_user_func_array is quite slow and user_access is a very common
     // callback, it is worth making a special case for it.
     if ($callback == 'user_access') {
@@ -637,6 +676,7 @@ function _menu_check_access(&$item, $map) {
       $item['access'] = call_user_func_array($callback, $arguments);
     }
   }
+  return TRUE;
 }
 
 /**
@@ -683,6 +723,11 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
   // If we are translating a router item (tabs, page, breadcrumb), then we
   // can always use the information from the router item.
   if (!$link_translate || ($item['title'] == $item['link_title'])) {
+    $title_arguments = menu_unserialize($item['title_arguments'], $map, $item);
+    if ($title_arguments === FALSE) {
+      // An error happened when loading the object. $item['access'] is now set to FALSE.
+      return;
+    }
     // t() is a special case. Since it is used very close to all the time,
     // we handle it directly instead of using indirect, slower methods.
     if ($callback == 't') {
@@ -690,7 +735,7 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
         $item['title'] = t($item['title']);
       }
       else {
-        $item['title'] = t($item['title'], menu_unserialize($item['title_arguments'], $map));
+        $item['title'] = t($item['title'], $title_arguments);
       }
     }
     elseif ($callback && function_exists($callback)) {
@@ -698,7 +743,7 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
         $item['title'] = $callback($item['title']);
       }
       else {
-        $item['title'] = call_user_func_array($callback, menu_unserialize($item['title_arguments'], $map));
+        $item['title'] = call_user_func_array($callback, $title_arguments);
       }
       // Avoid calling check_plain again on l() function.
       if ($callback == 'check_plain') {
@@ -773,7 +818,7 @@ function _menu_translate(&$router_item, $map, $to_arg = FALSE) {
     $tab_parent_map = explode('/', $router_item['tab_parent']);
   }
   for ($i = 0; $i < $router_item['number_parts']; $i++) {
-    if ($link_map[$i] == '%') {
+    if (isset($link_map[$i]) && $link_map[$i] == '%' && isset($path_map[$i])) {
       $link_map[$i] = $path_map[$i];
     }
     if (isset($tab_root_map[$i]) && $tab_root_map[$i] == '%') {
@@ -787,7 +832,13 @@ function _menu_translate(&$router_item, $map, $to_arg = FALSE) {
   $router_item['tab_root_href'] = implode('/', $tab_root_map);
   $router_item['tab_parent_href'] = implode('/', $tab_parent_map);
   $router_item['options'] = array();
-  _menu_check_access($router_item, $map);
+
+  if (!_menu_check_access($router_item, $map)) {
+  //if (isset ($router_item['_cached_object_map']) && $router_item['_cached_object_map'] === FALSE) {
+    // An error occurred loading an object.
+    $router_item['access'] = FALSE;
+    return FALSE;
+  }
 
   // For performance, don't localize an item the user can't access.
   if ($router_item['access']) {
@@ -917,14 +968,10 @@ function _menu_link_translate(&$item, $translate = FALSE) {
       $item['access'] = FALSE;
       return FALSE;
     }
-    // menu_tree_check_access() may set this ahead of time for links to nodes.
-    if (!isset($item['access'])) {
-      if (!empty($item['load_functions']) && !_menu_load_objects($item, $map)) {
-        // An error occurred loading an object.
-        $item['access'] = FALSE;
-        return FALSE;
-      }
-      _menu_check_access($item, $map);
+    if (!_menu_check_access($item, $map)) {
+      // An error occurred loading an object.
+      $item['access'] = FALSE;
+      return FALSE;
     }
     // For performance, don't localize a link the user can't access.
     if ($item['access']) {
@@ -970,8 +1017,15 @@ function _menu_link_translate(&$item, $translate = FALSE) {
  */
 function menu_get_object($type = 'node', $position = 1, $path = NULL) {
   $router_item = menu_get_item($path);
+
+  if (!is_array($router_item['load_functions'])) {
+    $router_item['load_functions'] = unserialize($router_item['load_functions']);
+  }
   if (isset($router_item['load_functions'][$position]) && !empty($router_item['map'][$position]) && $router_item['load_functions'][$position] == $type . '_load') {
-    return $router_item['map'][$position];
+    $map = $router_item['map'];
+    if (_menu_load_objects($router_item, $map)) {
+      return $map[$position];
+    }
   }
 }
 
diff --git a/modules/menu/menu.test b/modules/menu/menu.test
index 95e0ee9..d90684e 100644
--- a/modules/menu/menu.test
+++ b/modules/menu/menu.test
@@ -216,6 +216,21 @@ class MenuTestCase extends DrupalWebTestCase {
     $this->verifyMenuLink($item2, $node2, $item1, $node1);
     $this->verifyMenuLink($item3, $node3, $item2, $node2);
 
+    // Verify first level of menu tree.
+    $parameters = array();
+    $tree = menu_build_tree($menu_name, $parameters);
+    foreach ($tree as $tree_item) {
+      $has_title = !empty($tree_item['link']['title']);
+      $this->assertTrue($has_title);
+    }
+
+    // Load tree a second time with the same parameters so we get a cached tree.
+    $tree_second = menu_build_tree($menu_name, $parameters);
+    foreach ($tree_second as $tree_second_item) {
+      $has_title = !empty($tree_item['link']['title']);
+      $this->assertTrue($has_title);
+    }
+
     // Add more menu links.
     $item4 = $this->addMenuLink(0, 'node/' . $node4->nid, $menu_name);
     $item5 = $this->addMenuLink($item4['mlid'], 'node/' . $node5->nid, $menu_name);
