Like many people (based on different forum threads), I had the following error:

PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null: INSERT INTO {menu_router}

This post explains how I managed to solve it, in the hope that my technique will help others with the same sort of problem.

You can see a post I made trying to get help in the nearest thread I could find to match the issue. Notice there the huge size of the error message. This was actually what was confusing me completely and in the end what proved to be the key to the solution. I just didn't understand this error message properly, in particular how the INSERT has 22 items

INSERT INTO {menu_router} (path, load_functions, to_arg_functions, access_callback, access_arguments, page_callback, page_arguments, delivery_callback, fit, number_parts, context, tab_parent, tab_root, title, title_callback, title_arguments, theme_callback, theme_arguments, type, description, position, weight, include_file)

and yet there were 459 VALUES!! What the?!? I didn't get it.

I carefully made a list of the parameters, noting carefully the array index, i.e.:

  • 0 => path
  • 1 => load_functions
  • 2 => to_arg_functions
  • ...
  • 13 => title [since this was the one mentioned in the error message, I didn't go any further]

I then looked at the 13th place in the error message, but it didn't make sense, because it wasn't null:

[:db_insert_placeholder_13] => Taxonomy term

When I looked at the code referred to (in includes/menu.inc line 3834 in Drupal 7.26), I eventually saw the explanation of the strange error message and the key to solving things:

<?php
function _menu_router_save($menu, $masks) {
...
   
// Execute in batches to avoid the memory overhead of all of those records
    // in the query object.
   
if (++$num_records == 20) {
     
$insert->execute();
     
$num_records = 0;
    }
?>

The INSERT is actually inserting 20 records at a time, and that explains why there are 20 times more VALUES than there are column names.

The solution was then simple: I commented out the bundling of 20 items so I left just the "$insert->excute()" :

<?php
function _menu_router_save($menu, $masks) {
...
   
// Execute in batches to avoid the memory overhead of all of those records
    // in the query object.
//     if (++$num_records == 20) {
     
$insert->execute();
//       $num_records = 0;
//    }
?>

Now, when I ran this modified code, my error message was 20 times simpler, and more importantly, it was clear and obvious exactly what was causing the error:

PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null: INSERT INTO {menu_router} (path, load_functions, to_arg_functions, access_callback, access_arguments, page_callback, page_arguments, delivery_callback, fit, number_parts, context, tab_parent, tab_root, title, title_callback, title_arguments, theme_callback, theme_arguments, type, description, position, weight, include_file) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15, :db_insert_placeholder_16, :db_insert_placeholder_17, :db_insert_placeholder_18, :db_insert_placeholder_19, :db_insert_placeholder_20, :db_insert_placeholder_21, :db_insert_placeholder_22); Array ( [:db_insert_placeholder_0] => flipcard/quiz/%/progress/level-1 [:db_insert_placeholder_1] => a:1:{i:2;N;} [:db_insert_placeholder_2] => [:db_insert_placeholder_3] => user_access [:db_insert_placeholder_4] => a:1:{i:0;s:27:"flipcard authenticated user";} [:db_insert_placeholder_5] => flipcard_levels [:db_insert_placeholder_6] => a:2:{i:0;i:2;i:1;i:4;} [:db_insert_placeholder_7] => [:db_insert_placeholder_8] => 27 [:db_insert_placeholder_9] => 5 [:db_insert_placeholder_10] => 1 [:db_insert_placeholder_11] => flipcard/quiz/%/progress [:db_insert_placeholder_12] => flipcard/quiz/% [:db_insert_placeholder_13] => [:db_insert_placeholder_14] => t [:db_insert_placeholder_15] => [:db_insert_placeholder_16] => [:db_insert_placeholder_17] => a:0:{} [:db_insert_placeholder_18] => 132 [:db_insert_placeholder_19] => [:db_insert_placeholder_20] => [:db_insert_placeholder_21] => 1 [:db_insert_placeholder_22] => ) in _menu_router_save() (line 3837 of /var/www/includes/menu.inc).

Now that the 19 irrelevant inserts have been cleared away, I can see immediately that indeed [:db_insert_placeholder_13] is blank (the "title" field), and I can also see clearly from [:db_insert_placeholder_0] what the path is which is causing the problem (flipcard/quiz/%/progress/level-1 in this case), and thus exactly which module was causing the problem in my case.

In this case (and yours will surely differ), it was my module Flipcard. Oops. But it meant I was immediately able to go straight to the part of the code which I had indeed recently changed, and true enough I had accidentally removed the filling in of the title field.

So, to summarise, if you are having a similar problem, try changing the insert to only insert 1 record at a time and it will make the solving of the source of the problem almost trivial.

Comments

WorldFallz’s picture

brilliant explanation of the troubleshooting process-- you should consider adding it to the documentation section at https://drupal.org/node/201875.

Thanks for posting!

_
Don't be a Help Vampire - read and abide the forum guidelines.
If you find my assistance useful, please pay it forward to your fellow drupalers.

Jaypan’s picture

Nice debugging! Figuring out things like that makes the difference between someone who uses Drupal and someone who masters it!

drupalshrek’s picture

Thank you WorldFallz and Jaypan for your kind and encouraging words!

As suggested, I have now added a page explaining how to deal with this error to the Error messages page

drupalshrek
Please fill in my Learning a foreign language questionnaire if you have a moment.

drupalshrek’s picture

For completeness, here is how the code with the 'title' not being set before and after fixing the bug.

Before fixing the bug. Note that the $title variable never gets set:

<?php
/**
 * Implements hook_menu().
 */
function flipcard_menu() {
...
 
// Each of the Level N words secondary level tabs
 
for ($level = (FLIPCARD_UNSTARTED_LEVEL + 1); $level <= FLIPCARD_MAX_LEVEL; $level++) {
    if (
$level == (FLIPCARD_UNSTARTED_LEVEL + 1)) {
     
$type = MENU_DEFAULT_LOCAL_TASK;
    }
    else {
     
$type = MENU_LOCAL_TASK;
    }
   
$items['flipcard/quiz/%/progress/level-' . $level] = array(
     
'type' => $type,
     
'title' => $title// Not set anywhere!!
     
'page callback' => 'flipcard_levels',
     
// Parameter 2 is taxonomy id
      // Parameter 4 is the level text
     
'page arguments' => array(2, 4),
     
'access arguments' => array('flipcard authenticated user'),
     
'weight' => $level,
    );
  }
?>

In this fixed code, the $title variable gets set:

<?php
/**
 * Implements hook_menu().
 */
function flipcard_menu() {
...

 
// Each of the Level N words secondary level tabs
 
for ($level = (FLIPCARD_UNSTARTED_LEVEL + 1); $level <= FLIPCARD_MAX_LEVEL; $level++) {
    if (
$level == (FLIPCARD_UNSTARTED_LEVEL + 1)) {
     
$type = MENU_DEFAULT_LOCAL_TASK;
    }
    else {
     
$type = MENU_LOCAL_TASK;
    }
    if (
$level == FLIPCARD_UNSTARTED_LEVEL) {
     
$title = "Unstarted";
    }
    else {
     
$title = 'Level ' . $level;
    }
   
$items['flipcard/quiz/%/progress/level-' . $level] = array(
     
'type' => $type,
     
'title' => $title,
     
'page callback' => 'flipcard_levels',
     
// Parameter 2 is taxonomy id
      // Parameter 4 is the level text
     
'page arguments' => array(2, 4),
     
'access arguments' => array('flipcard authenticated user'),
     
'weight' => $level,
    );
  }
?>

drupalshrek
Please fill in my Learning a foreign language questionnaire if you have a moment.

leymannx’s picture

Had the same error. I use Aurora and finally had to rename the vendor folder to .vendor to prevent breaking Drupal/Drush. Read more here https://github.com/Snugug/generator-aurora/issues/4

rankinstudio’s picture

This totally bailed me out! Thanks!

kenorb’s picture

kenorb’s picture

To find the broken entity, you may try the following drush command:

drush eval 'foreach (entity_get_info() as $entity_type => $entity_info) { empty($entity_info['label']) && var_dump($entity_type, $entity_info); };'
hongquan’s picture

I want to thank you thousand times :)

flyke’s picture

I had the same problem with the long error message and i could not find out where it came from. It happened usually after I did drush cc all. After reading your post, I realized the first place to start looking was in our custom modules where we set a title. So in our modules/custom I just searched for this string in code (without double quotes): "'title' =>".

I then checked all the titles which had a variable after that instead of a string or an array. Luckily there were only two of those. One of them had: 'title' => $user->name

I still cannot explain why after calling global $user, sometimes $user->name is null, but that's Drupal for you: sometimes things dont work. But I easily solved my problem by adding an ampty string, so title would never be null if for some weird reason $user->name is null:
'title' => ''.$user->name,

And that is how I solved my comparable issue. You really pointed out the way for where to start looking, thanks !

malcomio’s picture

I had a very similar error which was blocking progress on my site, and really slowing down page loads.

Whenever I cleared the cache, I was getting a ridiculous number of these errors.

Similar to the comment in #619542-142: Malformed theme .info files break menu_router generation I was running some Grunt tasks which depended on Bower and NPM components. These files included some .info files, which bore no relation to Drupal.

I wonder if there's any way of excluding specific directories from being searched for themes and modules...

malcomio’s picture

Turns out that applying the patch at #2329453-8: Ignore vendor folders to improve directory search performance fixed the issue for me.