Describe the bug
I expected to have onError event triggered as long as there was an error.
Talking to @ehmicky it currently has this behavior:
I was expecting the onError to only be triggered when the build fails, and since failPlugin() does not make the build fail, it should not trigger onError
I believe this may add confusion or inconsistencies if an error that exists doesn't trigger onError if utils.build.failPlugin is used.
Here is what this looks like in the build logs:
I think if this is what you expected to happen, other users might expect it too, so we should consider triggering onError on failPlugin(). I would be curious what @verythorough or @erquhart thinks too?
Some pending questions I would have:
onError is guaranteed to happen at the end of the build (right before onEnd). I am assuming this would be the case here too, i.e. it would still wait for the build to complete to trigger onError? This would also keep it consistent with onEnd and onSuccess.onError is only triggered once and it is passed an error as argument. To do the above, we would need to either call onError once per failPlugin(), or to pass an array of errors instead as argument. What are your thoughts on this?Fixed by #1254:
onError is triggered by failPlugin()onError is still guaranteed to happen at the end of the buildonError is passed the last error instanceI wonder if this is a naming issue. Are there any other non-fatal errors that trigger onError, or is it only triggered when the build actually fails?
If the latter, it sounds like the hook should be called onFailure (which matches onSuccess), and it should _not_ include plugin failures that don't fail the build. (And then perhaps we consider adding some sort of onPluginFailure and/or onError events.)
I think there could be two things plugin authors might want to handle:
At the moment (since #1254), onError is triggered on both. Following your line of thoughts, this could make sense to distinguish between the two, e.g. onBuildFailure vs onPluginFailure.
Note that this would not be a completely trivial change as it might need a rework of how we orchestrate plugins. Also, any renaming requires some work to maintain backward compatibility.
Edit: some additional explanation on the current logic. (see #1774).
If a plugin throws an uncaught exception, uses utils.build.failBuild() or uses utils.build.cancelBuild():
onError and onEndonError events for all plugins are runIf a plugin uses utils.build.failPlugin():
onError and onEndonError event for the current plugin is runonError() is not run otherwise.
onEnd() is always run, regardless of whether the current or other plugins failed.
Edit 2: the new onSuccess behavior introduces the following changes:
onSuccess and onEnd do not make the build fail nor trigger onErrorThis behavior has been a source of confusion. I would suggest the following solution as a way to improve the current behavior:
onError should only mean "on build failure", not "on plugin failure". This means it would be triggered if any Build plugin fails before deploy. This implies errors in onSuccess and onEnd would not trigger onError.try/catch blocks instead. IMHO having to fallback to a generic onError callback is the wrong solution to the right problem. Instead, Build plugins should wrap any code that might fail with try/catch blocks. If they wish to cleanup resources, onEnd can be used. If it turns out try/catch is not good enough for some plugin authors, we can always add a new onPluginFailure event in the future.This should make documenting onError much easier as the event would then clearly be happening after onPostBuild and before onSuccess.
Also, that change would be pretty lightweight to implement and not require any plugins migration.
What do you think?
@ehmicky This would certainly make the documentation task easier for onError. I'd want to make sure that we include a tip or something in our docs to encourage authors of Build plugins to wrap any code that might fail with try/catch blocks.
@ehmicky I think that makes total sense. Also, when something is easier to document, it usually means it's easier to use!
@ehmicky Can you confirm the timeline for this change?
_If_ this is something that will get implemented in the next week or so, I plan to address the docs changes for this in this related, open docs PR: https://github.com/netlify/docs/pull/722.
_If_ this will sit for a while, while the other build plugin event handler changes go out, please let me know, and I'll update the docs in a separate PR at a later date.
This is something that I can implement in the first few days of next week, providing you all think this is a good idea. :+1:
Summarizing the new behavior.
onError, onSuccess and onEnd mean "on build error/success/end", not "on plugin error/success/end". They always happen in that order.
Plugins should rely on try/catch/finally for exceptions handling.
A plugin fails _without making the build fail_ when either:
utils.build.failPlugin()onError, onSuccess or onEnd event handler.When that happens, no other events for that specific plugin is run. Not even onError and onEnd. Plugins should use try/catch/finally blocks to handle exceptions, not onError nor onEnd.
Otherwise, most failures will make the build fail. This includes:
onPreBuild, onBuild or onPostBuild event handler failed. This includes uncaught exceptions and using utils.build.failBuild() or utils.build.cancelBuild().onError is triggered when the build failed. It means "on build failure" not "on plugin failure".
onSuccess is the opposite. It is triggered after the build succeeded.
onEnd is triggered after the build either failed or succeeded. It is useful for resources cleanup.
PRs at #1884 and #1885.
Most helpful comment
This is something that I can implement in the first few days of next week, providing you all think this is a good idea. :+1: