Just now I did a CVS update, wiped database, and installed Drupal with the minimal/expert install profile.

Then I went to the Modules page and checked "enable" on all of the optional core modules except Overlay. Clicked Submit.

I got this error message on the results page, and I didn't have any of my sidebars etc. (the only working link on the page was in the header bar (the home page link):

FieldException: Attempt to create a field instance that already exists. in field_create_instance() (line 634 of /opt/www/eclipsework/drupal/modules/field/field.crud.inc).

It seems that all of the modules were successfully installed, but there's obviously some problem here that needs to be fixed....

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

jhodgdon’s picture

I'm also getting this same error again whenever I clear my caches from the Performance page.

jhodgdon’s picture

I just did another wipe/cvs update/install. Error still occurring.

I put in a crude print statement in field_create_instance, and found that it is a collision between:

Array
(
    [label] => Comment
    [object_type] => comment
    [settings] => Array
        (
            [text_processing] => 1
        )

    [display] => Array
        (
            [full] => Array
                (
                    [label] => hidden
                    [type] => text_default
                    [settings] => Array
                        (
                        )

                    [weight] => 0
                    [module] => text
                )

        )

    [widget] => Array
        (
            [type] => text_textarea
            [settings] => Array
                (
                    [rows] => 5
                )

            [weight] => 0
            [module] => text
        )

    [required] => 
    [description] => 
    [weight] => 0
    [id] => 4
    [field_id] => 2
    [field_name] => comment_body
    [bundle] => comment_node_forum
    [deleted] => 0
)

and

Array
(
    [field_name] => comment_body
    [label] => Comment
    [object_type] => comment
    [bundle] => comment_node_forum
    [settings] => Array
        (
            [text_processing] => 1
        )

    [display] => Array
        (
            [full] => Array
                (
                    [label] => hidden
                )

        )

    [field_id] => 2
)

I don't know enough about that to continue debugging.

It's also interesting to me that after a while, the error messages stop happening.

yched’s picture

Odd. Cannot reproduce. Not a fresh-from-the-day install, though.

jhodgdon’s picture

The behavior I'm getting is that it happens a few times when I first install, and then I stop seeing the error message. It all seems very odd.

sun.core’s picture

Any updates?

jhodgdon’s picture

I just tried it (wipe everything, install from HEAD, expert profile, check all core modules except Overlay and click Submit).

I'm still getting the same error.

jhodgdon’s picture

Correction: The error message is slightly different now:

FieldException: Attempt to create an instance of field comment_body on bundle comment_node_blog that already has an instance of that field. in field_create_instance() (line 645 of /opt/www/eclipsework/drupal7/modules/field/field.crud.inc).

jhodgdon’s picture

Looking into this a bit more...
Places I see field_create_instance() being called with the 'comment_body' field:
1) comment_enable() in modules/comment/comment.install -- note that it's not in comment_install() but in comment_enable().
2) comment_update_7012() in modules/comment/comment.install
3) comment_node_type_insert() in comment.module

My guess is that #1 and #3 are conflicting (I don't think the update functions should be run if I'm doing initial module enable, correct?), but I'm not sure of the exact sequence of events... Let's see...

a) When I submit the modules form, system_modules_submit() calls http://api.drupal.org/api/function/module_enable/7
b) module_enable() first updates the modules db (setting status of each module to 1, indicating it's enabled). So at this time, both the blog module and the comment module will be enabled.
b) Then module_enable() calls each module's hook_enable(). Blog doesn't have one. comment_enable() at that time lists all content types with node_get_types(), finds 'blog' without a comment body field, and attaches the comment body field. Which would seem OK, except that node_types_rebuild() hasn't been called yet, so even though node_get_types() tells the comment module that the blog content type exists, it actually hasn't been run through node_type_save() yet.
c) Then module_enable calls a bunch of functions to reset caches, including node_types_rebuild(), which at this point calls node_type_save() on all the new content types (including blog), thus triggering the comment_node_type_insert() function, which doesn't bother to check to see if the comment body field already exists before attaching it. http://api.drupal.org/api/function/comment_node_type_insert/7

So. IMO, the problem is really that comment_enable() is assuming that the node types have been built, when in fact they haven't been built yet. Because comment_node_type_insert() should be able to assume it's getting a brand-new content type being saved for the first time (if it was a new content type, it should have been sent to hook_node_type_update() instead).

The fix is probably to have comment_enable() call node_types_rebuild() to ensure that the list of content types returned by node_get_types() is really accurate with what's been saved to the database. Or for node_get_types() to verify that everything is in the DB.

jhodgdon’s picture

Minimal steps needed to reproduce this issue:

a) Create a new space (wipe database, cvs get D7 HEAD)
b) Run install.php and choose the "minimal" install profile.
c) Visit the modules page (admin/modules/list)
d) Check "Blog" and "Comment" and submit the form.

You will see the following error:

FieldException: Attempt to create an instance of field comment_body on bundle comment_node_blog that already has an instance of that field. in field_create_instance() (line 645 of /opt/www/eclipsework/drupal7/modules/field/field.crud.inc).

jhodgdon’s picture

Title: FieldException when trying to enable modules » FieldException when trying to enable blog and comment modules for first time
jhodgdon’s picture

Assigned: Unassigned » jhodgdon

Thinking this over...

So the problem is that comment_enable() is assuming that node_get_types() is returning fully-created types (i.e. types that have been run through node_type_save()), because comment_node_type_insert() will take care of content types as they are created with node_type_save().

Actually, I think that in most cases, the functions that call node_get_types() and related functions will assume that the node types returned have been already run through node_type_save() at least once. So I'm going to create a patch that will assume that, but let the few cases where that isn't the case bypass that.

jhodgdon’s picture

Ugh.
Actually, I think that maybe the best thing is just for comment_update() to call node_types_rebuild() to make sure it's getting the right information.

jhodgdon’s picture

Status: Active » Needs review
FileSize
1.6 KB

Here's a patch that calls node_types_rebuild() in comment_update(), and adds doc to node_type_get_types() to warn others of this issue.

I've verified that with this patch, the reproduce steps in #9 do not produce the error message any more.

Just as a note, the reason I decided it wasn't a good idea to add automatic rebuild to node_type_get_types() is that it's really only an issue during module install, because node_types_rebuild() is always called during module install, and it would have added unnecessary overhead.

yched’s picture

Ugh. Thanks for great detective work !

node_type_get_types() returning types that have not been 'inserted' yet (hook_node_type_insert() yet to be called) does sound like a WTF.
Not sure of the best fix at this time of the day :-/.

jhodgdon’s picture

Yeah, it's a definite WTF. However, it does only come up at install/enable module time, since module_enable() calls node_types_rebuild() a bit farther down in the function.

er.

I guess node_types_rebuild() is called from system_modules_submit() and not from module_enable().

Hrmmph. _install_module_batch() doesn't call node_types_rebuild() either. Hopefully install.php does it at some point...

David_Rothstein’s picture

Any thoughts on whether it would be a good idea to take the node_types_rebuild() call out of system_modules_submit() and put it in module_enable() instead?

(That kind of thing is part of the overall project at #651086: Cache clearing is an ineffective mess in module_enable() and system_modules_submit() but who knows where that will end up, and if we have a specific bug here it seems like it might make sense to do it now...)

The installer should be covered, since it just calls drupal_flush_all_caches() at the end, as a catch-all for this kind of problem :)

jhodgdon’s picture

My feeling is that logically the best thing to do would be to build it into node_type_get_types() (or technically, the internal function that does the work for that and other functions), so that there would be no way to call node_type_get_types() and get the wrong answer (i.e. types that haven't been saved). This could probably be made fairly efficient with a flag saying "node types need rebuilding", which would have to be reset early in the module install process.

If that was done, then system_modules_submit() et all wouldn't even have to call node_types_rebuild(). Same thing could be done with menus, etc. if we had a hook_caches_are_all_wrong() or something like that, which is maybe what is being proposed in #651086: Cache clearing is an ineffective mess in module_enable() and system_modules_submit()?

But really, both of these seem like too big of a change for D7 at this point, which is why I proposed the patch in #13, which fixes the immediate problem with comment.module and documents the problem in the doxygen for node_type_get_types().

grendzy’s picture

Status: Needs review » Reviewed & tested by the community

I've verified that that this fixes the FieldException error. And the reply in #17 is convincing enough for me...

catch’s picture

#13 looks good until #651086 is fixed (which might be D8 at this point).

Dries’s picture

It feels odd to call node_type_rebuild() in comment module, rather than calling it directly after the node type was created. Any reason for that? In fact, I thought the analysis/recommendation in #11 was right on.

jhodgdon’s picture

Dries: See #17 for my best idea of how to actually fix this... Would you support this type of change this late in D7 development? I think that's the best place to put it.

My idea in #11 of a simple fix was negated once I looked deeper into the code (see #13).

Dries’s picture

I think we should try to fix this properly, even this late in the cycle. Seems like an important thing to get right.

jhodgdon’s picture

Status: Reviewed & tested by the community » Needs work

OK. I can take a look at it early next week.

Or someone else can take it on if they have time earlier -- just assign it to yourself so I know it's not my project any more. :)

jhodgdon’s picture

OK, I'll take this on now.

jhodgdon’s picture

Assigned: jhodgdon » Unassigned

Well, maybe not. I give up... The fixes I attempted did not solve the problem, and I'm not sure what to do...

Note that the easy way to reproduce the problem is in #9.

jhodgdon’s picture

FileSize
3.89 KB

For reference, here's the patch I attempted. With this patch, the error I get when following #9 steps to reproduce is:
"FieldException: Attempt to create an instance of field body without a bundle. in field_create_instance() (line 627 of /opt/www/eclipsework/drupal7/modules/field/field.crud.inc)."

So it looks like this solves one problem but creates another. I really don't know what to do at this point, besides going back to the hackish patch in #13 that Dries didn't like.

mradcliffe’s picture

The changes that were made in #553306: Make nodes have no body field by default. Remove deprecated APIs for body field so far cause the patch in #13 not to work.

Also, every time I try the blog/comment thing above it creates a field instance no matter what. If you uninstall comment it doesn't remove the instance tables. So if you keeping doing enable/disable/uninstall you'll end up adding two tables every time (install adds 4 tables, and uninstall removes 2).

field_data_comment_body_N
field_revision_comment_body_N
field_config_instance correspondingly has N instance entries.

If I comment out the _comment_body_field_instance_create from comment.install (line 73) there's no exception anymore. Where the heck are the instances getting created (still searching...)?

jhodgdon’s picture

If you comment out _comment_body_field_instance_create(), the instances on the blog node type will be created when node_type_save() is eventually called from node_types_rebuild(). That is why in #9 reproduce you get the error (without the patch): comment_enable() creates the instance, and then node_type_save() does it again. (See #8 above for complete explanation of this sequence.)

So removing that line would fix the problem, but break the intended behavior of that line: you want to add the comment field to *existing* node types that existed before this round of module enablings.

The problem is that in comment_install(), you want only the old types to be added to, not node types that have yet to be added to the database (i.e. the ones that are defined in newly-enabled modules' hook_node_info() implementations, which are not saved to the database until node_types_rebuild() is called).

Does that make sense?

The thing about having multiple instances that aren't removed is a separate issue and should probably be dealt with separately.

mradcliffe’s picture

Yeah, I just spent the last 15 or so minutes working that out in a post I was about to make. ;-)

It would work if blog node type was created after comment_enable instead of before.

Is node_type_get_types() grabbing the blog node type before it's even created?

Edit: Yes. Blog node type isn't added to node_type database yet, but since the module code is loaded it's returned as a valid node type.

mradcliffe’s picture

Status: Needs work » Needs review
FileSize
890 bytes

After talking on IRC and getting some food I'm not sure we should rewrite something as big as node_types_rebuild and _node_types_build this late in the development cycle. It may have some hefty effects.

Can't we just make it a one line fix and not do anything on $info->is_new and $info->disabled?

jhodgdon’s picture

a) Does that fix the problem? It looks fairly reasonable...

b) If we use this tactic rather than changing how node_type_get_types() et al work, I would advocate adding doc to all of the node_type_get_* functions, as well as _node_types_build(), detailing what their return value is in the case where the db is not synchronized with the hooks, and explaining that it wouldn't be synchronized during module install/enable hooks.

mradcliffe’s picture

Yes, it prevents comment.install from adding the field, and lets it be done in hook_node_type_insert instead.

Probably more documentation on using is_new and is_disabled to advocate using it.

vwX’s picture

Status: Needs review » Reviewed & tested by the community

I've reviewed and tested this patch on the latest HEAD and can confirm that it does indeed fix the exception issue. I agree on the documentation comments, always a good thing.

mradcliffe’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
2.91 KB

Thank you for your help, vwX.

Attached is some documentation about is_new and disabled to _node_types_build and to point to that method from the docs of other methods.

I'll set it to rbtc again after testing comes back.

Status: Needs review » Needs work

The last submitted patch, 682552-new-disabled-condition-docs.patch, failed testing.

mradcliffe’s picture

Status: Needs work » Needs review
FileSize
2.91 KB

That looks like a testing bot goof. It passes Tracker module test here with updated head. Re-rolling patch just in case.

jhodgdon’s picture

Status: Needs review » Needs work

I think the doc could be better...

- The field explanation needs more detail, and should explain
- Several of the functions do not give you those fields to check.
- Just having @see _node_types_build() and having the doc on node_types_build() does not seem like a very good way to warn users of those functions, who might not bother to jump to that other page/function and see the doc. So I think the warning should be on all of those node_type_get_* functions too.

I can give the doc a work-over on Monday, unless someone gets to it before me....

jhodgdon’s picture

Status: Needs work » Needs review
FileSize
5.66 KB

Here's a new patch, with more doc explanation, preserving the fix for the bug from #36.

jhodgdon’s picture

FileSize
5.35 KB

Whoops. Try this one (a few minor typographical corrections in the doc)

mradcliffe’s picture

Status: Needs review » Reviewed & tested by the community

Thank you for expanding on that, jhodgdon. I think the expanded docs to _node_types_build will definitely help for debugging any future issues that people run into.

I'm going to move this to RTBC from vwX's, your, and my testing, and hopefully Dries can take a look at this from #22 to get this critical issue done.

grendzy’s picture

Status: Reviewed & tested by the community » Needs work
+++ modules/node/node.module	29 Mar 2010 15:08:46 -0000
@@ -659,9 +668,23 @@
+ * These two information sources are be synchronized during module installation
+ * until node_types_rebuild() is called.

This doc change isn't clear...

136 critical left. Go review some!

jhodgdon’s picture

be -> not

Sorry about that.

mradcliffe’s picture

Status: Needs work » Needs review
FileSize
5.63 KB

Made change, read over again, ran by spellcheck, re-rolled.

jhodgdon’s picture

Status: Needs review » Needs work

One more fix needed:

- * All new or non-modified module-defined node types are saved to the database.
+ * All new module-defined node types are saved to the database via a call to
+ * node_type_save(), and obsolete ones are deleted via a call to
+ * node_type_delete(). See _node_types_build() for an explanation of the new 
+ * obsolete types.

Last line
obsolete types -> and obsolete types.
(again, my fault, sorry)

I must run out, so cannot reroll right now, sorry.

jhodgdon’s picture

Status: Needs work » Needs review
FileSize
5.63 KB

Oh wait, I don't have to leave for an hour. Here's a revised patch.

catch’s picture

Status: Needs review » Reviewed & tested by the community

Patch looks good, fix is one line, lots of comments. This will be either annoying or impossible to test until we stop relying on the default profile for testing, so it should go in as a quick fix.

Dries’s picture

Status: Reviewed & tested by the community » Fixed

OK ... Committed to CVS HEAD. Thanks.

mradcliffe’s picture

Status: Fixed » Needs review
FileSize
1.15 KB
939 bytes

Oh, crap. I feel terribly dumb. There are cases where is_new and disabled are undefined leading to the following warnings.

If node_set_type_defaults() isn't called and the type isn't new, then is_new is never defined. Even if it is called disabled isn't defined either (as false). When looking at existing node types is_new isn't defined (as false).

We probably need even more documentation, tests, and defining these two variables to make sure that nothing is left undefined. I think that having undefined variables in certain cases is a bit odd if they're boolean. It also seems a bit blah that one is set to FALSE and the other is set to 1/0. Should they remain consistent?

The other idea, since they're either in a defined or undefined state, is to change the ! to isset().

    *  Notice: Undefined property: stdClass::$is_new in comment_enable()  (line 72 of /Applications/MAMP/htdocs/drupal7/modules/comment/comment.install).
    * Notice: Undefined property: stdClass::$disabled in comment_enable() (line 72 of /Applications/MAMP/htdocs/drupal7/modules/comment/comment.install).

Attaching a couple of patches, setting to review for a test, and then setting back to needs work afterward.

mradcliffe’s picture

Status: Needs review » Needs work

The other conditionals in node.module with is_new and disabled use isset().

Should additional documentation be split into two places?

node_type_set_default (which i dyslexiafied last post ;-) for is_new and _node_types_build for disabled? Or just expand the documentation for _node_types_build?

mradcliffe’s picture

Status: Needs work » Needs review
FileSize
941 bytes

UGH, I just realized that the defined patch above is backwards. Whoops, sorry for spam.

catch’s picture

Status: Needs review » Reviewed & tested by the community
Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to CVS HEAD. Thanks.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.