While running a Drupal 7 to Drupal 8 migration, if there is an image field involved, you will get the error:
Error: Call to a member function getFileUri() on a non-object in /var/www/docroot/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php, line 318
$image = \Drupal::service('image.factory')->get($this->entity->getFileUri());
This line is executed when there so no height or width defined after the parent:presave(), and so assumes we need a new Image entity.
But the "entity" property of $this is never there, at least at this point. Even when we have a valid image entity, when just saving an image with an image field normally, the "entity" property never exists.
We need another way to get the file URI, or another way to create the image entity.
| Comment | File | Size | Author |
|---|---|---|---|
| #84 | 2705925-84.patch | 7.52 KB | heddn |
Comments
Comment #2
tregonia commentedI have encountered this issue, but my error is not exactly like main error.
Sightly different in that the function is called on null, not a non-object.
Comment #3
quietone commentedI believe I have encountered this error, but on d6 migrations. It happens during the fid lookup in core/modules/file/src/Plugin/migrate/process/d6/CckFile.php. See #2640842: Make related file migration ID configurable in d6_cck_file
Comment #4
skuark commentedI also am experiencing this bug. With 8.0.x it worked properly. The problem have started after update to 8.1.x.
Comment #5
esclapes commentedI am also finding this same error on a custom d6 to d8 migration.
My destination is a node bundle (article) and the corresponding field should have been skiped as some
fidare empty and the field is not required. This is my field migration config:I have hacked the
ImageItemadding a check for$this->entityin the conditional. It however is trying to insert a empty string in as thefidso I guess the migration is trying to save afield_imagefield that should be considered empty on the entity destination.Not sure how to debug further. I think this is more relevant to the migration system, though.
Comment #6
hassan2 commentedHaving the same issue, migrating d > 8. Drupal 8.16 core.
Fatal error: Call to a member function getFileUri() on a non-object in core/modules/image/src/Plugin/Field/FieldType/ImageItem.php on line 317.
Comment #7
hassan2 commentedI have deleted the images from the content type that i wanted to migrate and rest of fields could be migrated. Make sure that content types field format are set to default. eg. if you have plain text it will not be migrated.
Comment #9
attheshow commentedAs @sclapes suggested, changing the line to read:
if ((empty($width) || empty($height)) && $this->entity) {appears to work and allows the upgrade to proceed.
Comment #10
mikeryanAlthough triggered during migration, the root problem is in the image module's ImageItem class. I'm guessing the reason it's not been reported outside of migration is that existing cases are always setting the dimensions explicitly - next step should be a fail test leaving the dimensions empty.
Comment #11
heddnI'm seeing this triggered when
$is_file = is_a($this->entity, \Drupal\file\FileInterface::class);returns FALSE.$this->entityisn't empty, it is just a string and not an File class.Comment #12
heddnComment #15
heddnComment #17
heddnComment #18
rcodinaPatch on #17 solves the issue. Now the migration runs normally. In my case, now I can continue to debug my migration ;)
I applied the patch directly on 8.2.0. Thanks @heddn
Comment #19
heddnBased on #18, marking as RTBC
Comment #20
catchWhen does
$this->entityget set to a string and not an entity? Should we be enforcing it gets set further up the stack instead?Comment #21
heddnre #20: it comes from a migration from D6/D7. And right now migrations do not validate any data, they just stuff it into the destination. After #2745797: Add option to content entity destinations for validation, we could turn on that validation, but that has its own set of problems.
Comment #22
mlncn commentedConfirming this is necessary and works for the significant migration from Drupal 7 use case! Seems a small and reasonable validation in ImageItem. Setting back to RTBC.
Comment #23
catchIf it happens with migrations, does that mean we're missing test coverage for migrations? I'd rather see that than the test hunk added here, but in general still feels like the wrong approach.
Comment #24
heddnA few thoughts about #23.
TLDR; Yes, it would be nice to catch this in migrate. No, it isn't very likely to happen very soon. In the mean time, this has good test coverage and fixes a know problem faced by many sites today.
Comment #25
catchThat's not just a migration issue then.
Also this hides bad data with no error though on those existing sites - so for example if you had a bug where files go missing (which we do have several of due to file_usage being broke), they could continue to go missing and nothing's going to tell you unless you actually spot the specific file. Maybe switching from an exception to E_USER_WARNING might be appropriate.
Comment #26
heddnI really like that idea. Right now it isn't an exception, it is a fatal so improving that to a E_USER_WARNING is much nicer. Then the migration won't fail half-way through and all those warnings can be reviewed in the logs and fixed on a per file basis.
Comment #27
gerzenstl commentedI can confirm that the patch submitted on #17 works for a D7 -> D8 migration (v8.2.0).
I agree, a E_USER_WARNING for this case would be a good thing to have.
Comment #28
heddnAssigning to myself to work on this.
Comment #29
ada hernandez commentedadding E_USER_WARNING for preSave()
Comment #31
heddnComment #32
heddnComment #33
ada hernandez commentedthanks! @heddn
Comment #35
heddnComment #36
kcolwell commentedI can confirm that the patch submitted on #17 works.
Many thanks!
Comment #37
quietone commentedRestesting since the failed test passes locally.
Comment #38
crozilla commentedHaving this same problem on my upgrade. Tried the latest patch, but it failed with errors. (I am using PHP 5.6 if that matters.)
Comment #39
heddnre #38: what errors?
Comment #40
crozilla commentedI got this error message:
An AJAX HTTP error occurred.
HTTP Result Code: 200
Debugging information follows.
Path: /my_drupal_directory/batch?id=14&op=do_nojs&op=do
StatusText: OK
ResponseText:
Fatal error: Call to a member function getFileUri() on null in .../core/modules/image/src/Plugin/Field/FieldType/ImageItem.php line 317
Comment #41
heddnre #40, the error you are getting shows the patch is *not* applied. That error should go away once the patch here is added. It should get converted into a E_USER_WARNING error.
Waiting on more feedback than just #36, but I think this is close to RTBC.
Comment #42
wonder95 commentedThe patch works for me in a D6 -> D8 migration.
Comment #43
heddn#36 & #42 confirm this fixes things. For #38, there seems to be a question if the patch was properly applied. Based on that, I think this can proceed to RTBC, yes?
Comment #45
ericpughWhich patch is being reviewed?
Comment #46
heddnre #45: the patch in #35
Comment #47
sebi commentedPatch #35 removed the error for me. In our particular case, we were saving images via inline-entity-form from the panelizer IPE interface.
Comment #48
berdirThe patch in #35 is failing tests?
Comment #49
berdirTo be more exact, that test is wrong, a trigger error is not an exception, and it can't be tested like that.
I'm not sure if it even makes sense to trigger an error, I'd just check it and move on if it's not valid. Testing that it does *not* fail is enough.
Comment #50
heddnAssigning to myself to work on tests.
Comment #51
heddnSo, the test in #35 is kinda doing the right thing. PHP Unit causes errors to be thrown as exceptions. See https://phpunit.de/manual/4.8/en/appendixes.configuration.html. So the warning gets converted to an exception in the presave and gets converted to a EntityStorageException. The failure is coming from a fixture in the node migration that doesn't have a valid File migration already run.
It seems like #2488860: Bring phpunit bridge into drupal and use it for unit tests and simpletest to handle Deprecation might be able to help with some of this, except its focus right now is really user deprecated. Not a general solution for handling these warnings.
Comment #52
heddnGoing to put this to the side for now as I've run out of time. I tried adding d7_file to the list of executed migrations in d7/MigrateNodeTest. But that didn't fix things. I wonder if we have a bad set of data in the fixtures for D7 files.
Comment #53
mile23The problem here is that
setExpectedException()means 'when you get this exception, the test is over.'You could change the following
assertEmpty()calls toassertNotEmpty()and the test will still pass, because they're after the exception. Basically, this patch swallows all the assertions after the exception.So: Verifying the error-throwing behavior should be a separate test method. You'll have to use a try/catch block in order to check the values of height and width after the exception is thrown.
You don't really need the error handler fancy stuff from #2488860: Bring phpunit bridge into drupal and use it for unit tests and simpletest to handle Deprecation, just a try/catch.
I'd much rather see an exception thrown here to signal that there's no image entity when you expect one, but yah, what a pain for migrations.
Comment #54
heddnThanks for the hints on the exception catching Miles. Now to track down the failure in d7 node. It seems like a missing fixture for the file migration. But we'll have to dig into the migration to prove that first.
Comment #55
jibranI ran into the same problem on D8 and fixed it like this.
Comment #56
jibranAll we now need a failing test.
Comment #57
heddn@jibran, we've got basically the same patch earlier in #35. We've just been working on tests since then. I'm just now finishing what I think we need.
Comment #58
heddnInterdiff is against #35. Tests pass locally. Let's see what the bot says.
Comment #59
jibranNo offence but I think my apporoch is much simpler we don't need instanceof check and else is not necessary at all IMHO. In my patch checking isset($this->entity) will also set the entity if it is not set already.
Comment #60
heddnIf we don't have the else with E_USER_WARNING, then a migration will run and save with a silent error condition. Or that was the idea after introducing it in #25. I'm on the fence. It seems a couple folks want to fail more silently. I just want to get something committed.
Comment #61
jibranFailing silently is not new in migration world AFAICS.
Comment #62
heddnAnd un-assigning myself for now.
Comment #63
rchaplin commentedWhile it's not new, I would think having a message logged at minimum would be helpful, especially if you are doing custom migrations.
Moving forward on these types of issues, helps to bring Drupal into a more resonant environment and style with other platforms.
Thus 'migrating from x platform' to 'drupal' seems less of a risk, or at minimum, supportive.
Comment #64
berdirI think we can be a bit more specific here. We know there is a target_id or it would have been filtered out by the isEmpty() check but that target_id does point to anything.
I'd go with: Invalid file ID ($this->target_id) provided. Cannot determine the file dimensions.
I'd go with a more realistic scenario of a target_id 9999 or so. I think ther's an issue to filter out target_id 0 in some cases, then this wouldn't test anything useful anymore.
Becaus of that, I would also add an explicit fail if the save() goes through without throwing that exception.
Explicitly Looking for an EntityStorageException is a bit strange.
The actual exception is also in ->getPrevious() or so, then we can also assert what kind of exception is.
I think it would be useful to explain in a comment that PHPUnit re-throws php warnings as exceptions and that's why we are testing it here as that.
Comment #65
heddnComment #66
heddnAdd org credit.
Comment #67
berdirThanks for the quick update!
Lets see what the core committers have to say about it, looks ok to me now.
Changing the issue title to something that IMHO makes more sense, the property (as in field api property) does exist, but it could be empty.
Comment #68
catchIs the problem that there's no file on disk, or that the file on disk isn't an image? It looks like this is all handling the latter case, and if so 'is a file' seems like the wrong comment.
Same with 'Invalid file id 9999' - should it be "File ID 9999 points to a missing or malformed image"? Is it exactly the same code path for missing and malformed?
Comment #69
heddnGood points. This might help with that.
Comment #71
berdirNo, isEmpty() is not the same and doesn't work. The field isn't empty, it has an ID, that ID just doesn't point to an existing entity, so ->entity can't load it.
Comment #72
heddnThat's confusing then. Why would
instanceof EntityInterfacepass checks in isEmpty() butinstanceof FileInterfacenot pass in the ImageItem (see interdiff)? Running tests locally.Comment #73
heddnAhh, because:
Is this fine? It seems like maybe we should load the entity just to be safe. But that adds overhead too.
Comment #74
heddnYes, we do need to keep that logic in isEmpty() as-is. If we don't, it doesn't let us stub out records to non existent entities during a migration, then later on add the referenced item. And there are other use cases too I'm sure. Although I could see argument for the contrary too. But that's scope creep me thinks and a potential different issue.
So, back to adding the instanceof check in the presave.
Comment #75
joelpittetDoes this help D6 any longer @heddn?
Comment #76
heddnIt helps anywhere where there is garbage data. D6 tends to have more of it than D7, but its valid to use in a D7 site as well.
Comment #78
martinpsz commentedThis bug remains an issue, at least for me in D8.4.0 beta 1. Would the patch written in the D8.3 environment work with D8.4.0-beta1?
Comment #79
martinpsz commentedUpdate: patch for D8.3 works with D8.4. Thanks! I did, however, have to split the patch into 5 separate patches and applied on each of the files listed in the patch. That part wasn't clear for a rookie like myself going in. But maybe someone else will benefit from my mistake.
Comment #80
joelpittet@heddn, the triggered error should be one condition deeper, because it's response to the lack of width/height.
Comment #81
heddnHow this for an updated wording on the error?
Comment #82
joelpittetThanks that error message is better suited for the condition that fails.
Comment #83
edysmp@heddn #81 It seem a wrong patch.
Comment #84
heddnInterdiff seems right, let's see if this is correct now?
Comment #86
joelpittetAh, only read the interdiff, glad the testbot was doing it's job;)
Comment #89
catchCommitted/pushed to 8.5.x and cherry-picked to 8.4.x. Thanks!
Comment #91
trong.nguyen.tcec commentedNot working on Drupal 8.5.3. $this->entity is always NULL.
Comment #92
berdirThis does not fix $this->enitty being null, it just fixes having a fatal error *when* it is.
If you get those errors then it is working exactly as designed and you need to look into why it happens, could be faulty migrations or something deleting files that are still in use. Create a new issue with more details about your specific case if you can't figure it out. A support channel like Drupal Answers, Forum or slack might be better for that than an issue.
Comment #93
plato1123 commentedIs this fix being rolled into Drupal core? We shouldn't have this error be a show-stopper, right? It seems like my team is experiencing this issue due to simply different parts of our site being migrated at different times. We have a live Drupal 7 site and are slowly migrating it to Drupal 8. The files portion is one of the bigger portions so we don't re-run that every time we migrate another content type, which means a migrated content type has references to files that do not exist (has nodes that are newer than our file migration) so that this discrepancy seems to be causing these WSOD's/errors. It seems like it's less than ideal behavior to show end users error messages simply because a .jpg is missing here or there. Granted in our case we're mid-migration on a development site so there's no end users to have their sensitivities offended *grin*.