{"action":"created","issue":{"url":"https://api.github.com/repos/hapijs/hapi/issues/3381","repository_url":"https://api.github.com/repos/hapijs/hapi","labels_url":"https://api.github.com/repos/hapijs/hapi/issues/3381/labels{/name}","comments_url":"https://api.github.com/repos/hapijs/hapi/issues/3381/comments","events_url":"https://api.github.com/repos/hapijs/hapi/issues/3381/events","html_url":"https://github.com/hapijs/hapi/issues/3381","id":190704809,"number":3381,"title":"problems with Promise error handling in plugin registration","user":{"login":"5c077yP","id":1775958,"avatar_url":"https://avatars.githubusercontent.com/u/1775958?v=3","gravatar_id":"","url":"https://api.github.com/users/5c077yP","html_url":"https://github.com/5c077yP","followers_url":"https://api.github.com/users/5c077yP/followers","following_url":"https://api.github.com/users/5c077yP/following{/other_user}","gists_url":"https://api.github.com/users/5c077yP/gists{/gist_id}","starred_url":"https://api.github.com/users/5c077yP/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/5c077yP/subscriptions","organizations_url":"https://api.github.com/users/5c077yP/orgs","repos_url":"https://api.github.com/users/5c077yP/repos","events_url":"https://api.github.com/users/5c077yP/events{/privacy}","received_events_url":"https://api.github.com/users/5c077yP/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2016-11-21T12:38:28Z","updated_at":"2016-11-29T10:13:13Z","closed_at":null,"body":"Hey, \r\n\r\nI recently experienced an issue, when registering plugins using promise style and the plugin itself is not calling next with a proper Error Object. (Btw. I'm using the latest hapi version 15.2.0.)\r\n\r\nLet's say I'm registering plugins like this\r\n\r\n```js\r\nserver.on('start', () => console.log('server started successfully'));\r\n\r\nreturn server.register(plugins) // plugins is a list of plugins\r\n  .then(() => server)\r\n  .catch((err) => {\r\n    console.errror('could not register plugins', err);\r\n    process.exit(1);\r\n  })\r\n```\r\n\r\nnow imagine a bad plugin like this:\r\n\r\n```js\r\nclass BadError {\r\n  constructor(message) {\r\n    this.message = message;\r\n  }\r\n}\r\n\r\nconst badPlugin = {\r\n  register(server, options, next) {\r\n    return next(new BadError('bad-error'));\r\n  }\r\n};\r\n\r\nbadPlugin.register.attributes = { name: 'bad-plugin' };\r\n```\r\n\r\nand my list of plugins:\r\n\r\n```js\r\nconst plugins = [\r\n  /* .. `set-of-plugins-1` */,\r\n  { register: badPlugin },\r\n  /* .. `set-of-plugins-2` */\r\n]\r\n```\r\n\r\nWhat I thought is, that the server exits with code `1`, but what happens is the server starts, logs `server started successfully`. Now the server keeps running and all `set-of-plugins-1` are running but none of `set-of-plugins-2`.\r\n\r\nI checked the code and what I can tell is, that when calling the [`server.register`](https://github.com/hapijs/hapi/blob/master/lib/plugin.js#L158) function without a callback it's using the [`Promise.wrap`](https://github.com/hapijs/hapi/blob/master/lib/promises.js#L11), which re-calls the function with a callback. The register function now iterates through all plugins using [`Items.serial`](https://github.com/hapijs/items/blob/master/lib/index.js#L11), which itself breaks the serial flow whenever [any argument is passed to the next callback](https://github.com/hapijs/items/blob/master/lib/index.js#L22). But now this \"wrapped\" promise callback only expects one argument, which if an `Error` is rejected otherwise resolved. \r\n\r\nIn the described case, the plugin is unfortunately not calling next with a proper Error Object. Now the Items.serials breaks the flow, it calls the wrapped promise callback, but this callback itself doesn't recognise the error and instead just resolves the Promise. In my case this leads to a situation where all plugins before the \"bad\" one are registered fine, but the rest not and the server keeps running.\r\n\r\nIn this case I'm not controlling the errors, but I could check the error and wrap it into a real Error Object. Another solution could be to check in the  `server.register().then()` if any argument is passed to the `then` function it can only be an error (i think).\r\n\r\nOf course it would be great if from hapi itself this would be reported as an error, maybe there should be a 4th argument to `Promise.wrap` (e.g. `nodeStyleCallback = false`) which if a first argument is given rejects and otherwise resolves with the second argument, because in the case of the plugin registration [always only an error (or nothing) is passed to the callback](https://github.com/hapijs/hapi/blob/master/lib/plugin.js#L324), but of course i'm not super deeply into the code, so this is just an idea.\r\n\r\nThanks for reading.\r\n\r\n\r\n\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/hapijs/hapi/issues/comments/263530206","html_url":"https://github.com/hapijs/hapi/issues/3381#issuecomment-263530206","issue_url":"https://api.github.com/repos/hapijs/hapi/issues/3381","id":263530206,"user":{"login":"5c077yP","id":1775958,"avatar_url":"https://avatars.githubusercontent.com/u/1775958?v=3","gravatar_id":"","url":"https://api.github.com/users/5c077yP","html_url":"https://github.com/5c077yP","followers_url":"https://api.github.com/users/5c077yP/followers","following_url":"https://api.github.com/users/5c077yP/following{/other_user}","gists_url":"https://api.github.com/users/5c077yP/gists{/gist_id}","starred_url":"https://api.github.com/users/5c077yP/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/5c077yP/subscriptions","organizations_url":"https://api.github.com/users/5c077yP/orgs","repos_url":"https://api.github.com/users/5c077yP/repos","events_url":"https://api.github.com/users/5c077yP/events{/privacy}","received_events_url":"https://api.github.com/users/5c077yP/received_events","type":"User","site_admin":false},"created_at":"2016-11-29T10:13:13Z","updated_at":"2016-11-29T10:13:13Z","body":"somehow, the plugin returns a non Error error. The plugin registration loop stops/fails, but the promise of the `server.register` is resolved instead of rejected."}}
{"actor":{"display_login":"5c077yP"}}